I'm trying to make a paint program using swing. I have several shapes that the user can draw, including ellipses, lines, and rectangles. The shapes can drawn (using graphics2d) with various strokes (line thicknesses) and can be filled or unfilled.
I'm trying to implement a feature such that the user can click a shape with the right mouse button and drag it to move it.
My current strategy is to poll the array of shapes is reverse order (meaning in case of multiple shapes the most recently added one is selected).
Each shape implementation has a method called isSelected(int x, int y). I need to figure out how to determine whether a point falls on the shape. One challenge is that an unfilled shape such as an oval should not be selected when clicked inside, but should be selected if it is filled. Also, selection should respond to the thickness of a stroke. That is, a line should be selectable by clicking out the outer area if it uses a thicker stroke, not just the exact center of the line.
How on earth can I go about implementing this?
I am storing an array of a custom type which implements a 2d shape from this API
Then you could use:
Shape#contains(double, double)
Shape#contains(double, double, double, double)
Shape#contains(Point2D)
Shape#contains(Rectangle2D)
Depending on the information you have and what you want to check
Related
Since this is schoolwork I do not want a complete solution but rather a push in the right direction on what I should be looking at (hence why there is no code).
I got a class that handle mouse input. From that class I check if user hold down mouse button on a shape and then set that shape to be the active shape. While the user drags the shape the shape moves along with it and changes position accordingly. Once the mouse is released the selectedShape is set to null and the panel is being repainted.
I want that the selectedShape is on top of all the other shapes, so if the selected shape is being dragged over any other shape it is infront.
From what I understand the repaint() method just repaint them as they appear in the ArrayList and I need to use another method to paint that shape at top (visually speaking).
Where would I go about to add this functionality? In the MouseHandler class?
Classes included that are relevant:
MyShape - Parent class
Triangle, Square, Circle - Child - has its own draw(g) method and setPosition
MouseHandler - mouse movement and calls each shape to set new position.
MyShapeHandler - add/remove shapes from list
Panel - create instance of shapes and add to ArrayList using MyShapeHandler help method addShape. Draws each shape in ArrayList using drawComponent(g).
Window - JFrame that adds panel
You can try to rearrange the list so that the desired shape is first/last. Experiment and see if the shape's index in the list corresponds to its location in the front or back on the screen.
I have started a Java Paint program that seems to be working fine... There is just one problem. In my program I have it set up so that it repaint()'s ovals using MouseListener methods and overrides paintComponent(Graphics g). The problem is when I move my mouse to fast it begins to separate my ovals instead of making one smooth line when the mouse is dragged. Is there a way to fix this.
P.S. Keep in mind that I much rather use the fillOval method not the drawLine, because I still would like to set the stroke.
Thanks in advance
See Custom Painting Approaches for the two common ways to do painting. The example draws a Rectangle without problems as the mouse moves.
You can still set a stroke to use to draw a line between two points. You should store the previous mouse position and interpolate between the last position and the current position to create a Line2D shape. Then create a stroke that has the desired width of your oval, and apply that stroke to Graphics context, then draw the line. This link has more information about strokes and shapes.
If you really want to continue drawing ovals, you could interpolate along the line between the start/end points and draw multiple ovals in a loop.
Hi i am currently working on a Java app and i have to draw a little using Canvas, Graphics etc..
So if i click a point and drag it across, it should have a line drawn in between (Think of drawing a line in paint).
I am currently using fillRect and the question is, is there a way to fillRect from right to left? Or do i have to explicitly create a workaround for this?
fillRect() method needs four arguments, x, y, width, height. What you should do is just compute the values of those arguments. If you want to draw fillRect from right to left, you just need to decrease x and increase width perhaps as your mouse move. That's it.
To add to ntalbs excellent answer (+1).
Basically, when the user clicks a point, you need to store that as the anchor point. When they drag the mouse, you need to determine in which direction the mouse has dragged.
If the click.x > drag.x, the the drag.x becomes the x parameter for your rectangle, otherwise it's the click.x.
Width and height are simple determine as the difference between the click and drag points (taking into consideration which is larger ;))
So I have a large grid of 289 rectangles (17x17) and I need some way to change each one's color when they are clicked without having to make 289 different event methods. The retangles are colored based on a pattern:
if(y%2==0){
if(x%2==0)
g2.setColor(Color.WHITE)
else
g2.setColor(Color.BLUE)
}else{
if(x%2!=0)
g2.setColor(Color.WHITE)
else
g2.setColor(Color.RED)
}
I don't have any idea where I should start other than creating an big ArrayList of positions for each rectangle and their colors (such as {{0,0,Color.WHITE},{x,0,Color.BLUE}...etc};). I would use a for loop to create each of the rectangles using their parameters, but I don't know how I would create the ArrayList and Event method to detect which, if any, rectangle was clicked. How do I go about this?
EDIT:
I'm saying, how would I know which rectangle was clicked so I can change it's color? If it makes it easier, it's for a game where there are two players, red and blue. The board is made using the script above. When a player of a certain color clicks a white space the board changes that rectangles color to the players color, and that's where I have the trouble. I never have anyway to know when a player clicked one of the rectangles. How would I know when a player clicked a rectangle and how would I change it's color when clicked?
Add an instance of the same MouseListener to each component as it is constructed. There's an example here that changes a circle's color when the mouse is pressed.
Addendum: Based on your revised question, GridButtonPanel shows how a component may know its own coordinates, as well as how to reference a component based on its grid coordinates.
I have a JPanel which has a line, circle, etc. Now when I click on the line, will the event get reported as a line event or a general JFrame event. I need to be able to move the line if the user clicks on the line and moves it. Is this possible in Java2D?
Yes, but you will need to do some work (see java.awt.Shape). Basically you need to track a list of Shapes. The JPanel will recieve a mouse event, which you can translate to (x,y) coordinates. You can then call Shape.contains(x,y) to see if your various shapes were clicked on.
This will work well for Circle, Polygon, Arc, etc; however in the case of Line2D, it won't work as easily, but you can use Line2D.intersects() with a small rectangle around the mouse click (this is also good UI since you don't want to force the user to click exactly on a pixel that is hard to see).
There is no such concept as a "line event" unless you decide to implement one.
I would suggest adding a MouseListener and a MouseMotionListener to the Canvas or JPanel onto which your geometric shapes are drawn. Use the MouseListener's mousePressed(MouseEvent) callback to determine whether a given shape has been clicked on. Once you've established this, use MouseMotionListener's mouseDragged(MouseEvent) method to move and redraw the shape as the mouse cursor is moved.
Here's a simple example that demonstrates some of the techniques adduced in other answers.
I created a canvas markup library in Java a few years back and if you don't need to worry about transforms on the canvas (scaling, rotation, etc.) it is very easy to do.
Basically you just need to maintain a collection of the canvas shapes in a List (not a Set because Z order is probably important). The mouse listener will be on your canvas, and not on individual shapes. Add new items to the beginning of your collection (or iterate the list backwards later).
When the canvas receives a mouse down event iterate through your collection of shapes until you find one that is underneath your mouse coordinates. The easiest way to do this is to have your shapes implement an interface that defines some sort of hitPoint(int x, int y) method. That way your rectangles can implement a contains(), lines can do intersects() or graphics paths, you can account for some hit padding, etc.
Taking it one step further, your shapes should define their own draw(Graphics2D g) method so that you can easily do things like selection boxes, or set the paint mode to XOR to make shape 'moving' easier. The paintComponent method of your canvas would just have to iterate through your collection of shapes, calling shape.draw(g) on each one, passing in the graphics instance provided to the paintComponent method.