I understand how to use MouseMotionListener but I can't get the parameters right for drawing a rectangle and an oval.
This is my attempt at a rectangle, but the problem is if go to the left from the start point, the rectangle gets filled.
public void draw(Graphics g) {
g.drawRect((int)startPoint.getX(), (int)startPoint.getY(),(int)controlPoint.getX() - (int)startPoint.getX(), (int) controlPoint.getY() - (int)startPoint.getY());
}
This is my method for a circle, this seems to work fine. But i cannot alter it for it to form an oval.
public void draw(Graphics g) {
g.drawOval((int)startPoint.getX() - (int)controlPoint.distance(startPoint),((int)startPoint.getY() - (int)controlPoint.distance(startPoint)),
(int)controlPoint.distance(startPoint)*2,(int)controlPoint.distance(startPoint)*2);
}
The mousePressed must be the center(startPoint) and the drag should be the radius for an oval.
Let me for brevity change the variable names from startPoint to sp and from controlPoint to cp, then these changes to your code should do the trick:
int minX = Math.min(sp.x, sp.y);
int minY = Math.min(sp.x, sp.y);
int width = Math.abs(cp.x - sp.x);
int height = Math.abs(cp.y - sp.y);
g.drawRect(minX, minY, width, height);
g.drawOval(minX, minY, width, height);
The reason is that those methods should receive the top-left corner coordinates, as well as the width and height of the bounding box of the rect/oval being drawn.
Both Graphics#drawRect and Graphics#drawOval expect the parameters to mean x, y, width, height, not x1, y1, x2, y2...
Your start points may be greater than your end points, resulting in either or both the width and/or height been negative values (based on width = x1 - x2). The Graphics API doesn't like negative values very much. You will need to take this into consideration when calculating the starting points and size.
The crust of the problem can be solved using something like...
int minX = Math.min(currentX, startX);
int minY = Math.min(currentY, startY);
int maxX = Math.max(currentX, startX);
int maxY = Math.max(currentY, startY);
int x = minX;
int y = minY;
int width = maxX - minX;
int height = maxX - minX;
Take a look at java draws rectangle one way not both for a working example...
Related
With AWT I draw a border using java.awt.Graphics#drawOval and java.awt.Graphics2D#setStroke. For situations when the set stroke has a size bigger than the oval's diameter the resulting border is not like expected. In that situation the stroke overlaps the stroke of the other side of the circle: Circles north stroke overlaps the south stroke. AWT renders this overlapping in an XOR way as you can see in the following image.
What I'd expect instead is that the stroke overlapping is drawn in an OR way, so that in all situations when stroke width > circle diameter the center is black.
Is there a simple way I can set to change the behaviour to an OR overlapping mode, even when width or height of the circle (then its an ellipse) is not equal?
Same diameter (10px) with increasing stroke width:
Based on the solution that Marco13 mentioned in his comment I came up with this custom drawOval function. It basically switch from drawOval to fillOval once the stroke width is greater than the diameter. The position and dimensions for the fillOval function are calculated to match the drawOval output.
public static void drawOval(Graphics2D g2d, int strokeWidth, int x, int y, int width, int height) {
int minLength = Math.min(width, height);
int maxLength = Math.max(width, height);
if (minLength >= strokeWidth) {
g2d.drawOval(x, y, width, height);
} else {
int x1 = x - (strokeWidth - maxLength) / 2 - (maxLength / 2);
int y1 = y - (strokeWidth - maxLength) / 2 - (maxLength / 2);
int width1 = width + strokeWidth;
int height1 = height + strokeWidth;
g2d.fillOval(x1, y1, width1, height1);
}
}
This is how it looks like
I am drawing a series of rectangles on a Canvas. The rectangles are supposed to move on an angle. For some reason, when they move, they scale up:
xPos += xSpeed;
yPos += ySpeed;
updateBounds(xPos, yPos, width, height);
My UpdateBounds method:
public void updateBounds(double x, double y, double w, double h) {
bounds.setRect(x, y, w, h);
}
Bounds is a Rectangle2D object.
And my Drawing method:
g.fillRect((int) bounds.getX(), (int) bounds.getY(),
(int) bounds.getMaxX(), (int) bounds.getMaxY());
Why am I getting this behaviour?
Graphics.fillRect() accepts a width and height parameter, not the largest x and y position of the rectangle to draw.
The third and fourth parameters to fillRect should be Rectangle2D's getWidth() and getHeight().
As a reference, a link to what getMaxX() would give you.
rec.set(0, 0, canvas.getWidth(), canvas.getHeight()/2);
It creates the rectangle as expected, I know 0, 0 is x, y but how do I set y to the bottom so it stays at the bottom on all devices with different size?
you haven't given much information, so I'll state my assumptions
the x,y would be the top-left of your shape, so assuming that the canvas is the full height of the device:
x - 0, as you want the shape to start on the left edge, y - the height of the device minus the height of the shape
your code would be
int width = canvas.getWidth();
int height = canvas.getHeight()/2;
int x = 0;
int y = canvas.getHeight() - height;
rec.set(x, y, width, height);
Below I have a simple method for painting a set of objects onto an java.awt.applet.Applet. I thought this was very straightforward, but it ends up only painting the objects as a single pixel at the top-left corner of the applet. The idea is that the Display class takes in a set of fairly lightweight objects that extend GameObject and contain information about their location, size, and appearance on the screen, and then draws them pixel-by-pixel onto the applet, stretching and positioning them proportionally depending on the specified display height and width. In testing this, I set the width and height of the Display to 128, and pass two objects to the Display, which are both 32-pixel squares (both return 32 for getWidth() and getHeight()), one is red and returns 24 for getX() and getY(), and the other is blue and returns 16 for getX() and getY(). I put the Display in a javax.swing.JFrame and use a java.awt.BorderLayout to ensure it fills the frame (I use add(display, java.awt.BorderLayout.CENTER); within the aforementioned javax.swing.JFrame).
As far as I can tell, this should be paining a blue 32-pixel square that's 16 pixels from the top and left edges and a red 32-pixel square that is either obscured by or obscuring part of the other one. However, all I get is a single red or blue pixel in the top-left corner of the Display. This is consistent no matter how big or small the window is.
Code
public class Display extends java.awt.applet.Applet
{
private int w,h;
private ArrayPP<GameObject> gameObjects;//ArrayPP is my way of making a dynamically expanding array. It is similar to Vector, but has many more useful methods I use elsewhere.
public Display(int width, int height)
{
w = width;
h = height;
}
public void addGameObject(GameObject go)
{
gameObjects.add(go);
}
public void refresh(java.awt.Graphics g)
{
int x, y, w, h;
final int W = getWidth(), H = getHeight();
for (GameObject go : gameObjects)//Draw all objects
for (x = go.getX(), y = go.getY(), w = go.getWidth() + x, h = go.getHeight() + y; y < h; y++)//Draw all lines of the object
for (x = go.getX(); x < w; x++)//Draw all the pixels on this line of the object
{
g.setColor(go.getColorForPixel(x, y));
g.fillRect((x / this.w) * W, (y / this.h) * H, w/W, h/H);
}
}
}
public interface GameObject
{
public int getX();
public int getY();
public int getWidth();
public int hetHeight();
public java.awt.Color getColorForPixel(int x, int y);
}
Question
Why is java.awt.Graphics.fillRect(int x, int y, int width, int height) only painting the topleft corner of the applet?
Solution
The solution lies in the line that reads
g.fillRect((x / this.w) * W, (y / this.h) * H, w/W, h/H);
wherein integer calculations cause all values to be 0. The solution is as follows:
g.fillRect((int)(((float)x/(float)this.w) *W),
(int)(((float)y/(float)this.h) *H),
(int)((float)W/(float)w),
(int)((float)H/(float)h));
The problem lies with
(x / this.w) * W
If x and this.w are both integers and x
Convert one or more of x and w to float to force a floting point division.
(int)(((float)x/(float)this.w) *W)
I have two points of circle and center of this circle. I want to draw an arc between these points. Method drawArc is to simple and doesn't fit my purpose.
Anybody help?
You can use Canvas.drawArc, but you must compute the arguments it needs:
Lets say that the center of the circle is (x0, y0) and that the arc contains your two points (x1, y1) and (x2, y2). Then the radius is: r=sqrt((x1-x0)(x1-x0) + (y1-y0)(y1-y0)). So:
int r = (int)Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
int x = x0-r;
int y = y0-r;
int width = 2*r;
int height = 2*r;
int startAngle = (int) (180/Math.PI*atan2(y1-y0, x1-x0));
int endAngle = (int) (180/Math.PI*atan2(y2-y0, x2-x0));
canvas.drawArc(x, y, width, height, startAngle, endAngle);
Good luck!
Graphics.drawArc expects the following parameters:
x
y
width
height
startAngle
arcAngle
Given your arc start and end points it is possible to compute a bounding box where the arc will be drawn. This gives you enough information to provide parameters: x, y, width and height.
You haven't specified the desired angle so I guess you could choose one arbitrarily.