Unexpected circle border drawn with AWT - java

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

Related

Java Canvas - Rectangle2D Scales when Moving

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.

How do you draw a rectangle and an oval using mouse dragging?

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...

How to align rectangle to bottom of any device?

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);

Android - Draw Bitmap within Canvas

I currently have a maze game which draws a 5 x 5 square (takes the width of screen and splits it evenly). Then for each of these boxes using x and y cordinates I user drawRect, to draw a colored background.
The issue I am having is I now need to draw an image within this same location, therefore replacing the current plain background colour fill.
Here is the code I am currently using to drawRect (a few example):
// these are all the variation of drawRect that I use
canvas.drawRect(x, y, (x + totalCellWidth), (y + totalCellHeight), green);
canvas.drawRect(x + 1, y, (x + totalCellWidth), (y + totalCellHeight), green);
canvas.drawRect(x, y + 1, (x + totalCellWidth), (y + totalCellHeight), green);
I would then also need to implement a background image for all the other squares within my canvas. This background will have simple 1px black lines drawn over the top of it, current code to draw in a grey background.
background = new Paint();
background.setColor(bgColor);
canvas.drawRect(0, 0, width, height, background);
Could you please advice if this is at all possible. If so, what is the best way I can go about doing this, whilst trying to minimise memory usage and having 1 image which will expand and shrink to fill the relvent square space(this varies on all the different screen sizes as it splits the overall screen width evenly).
Use the Canvas method public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint). Set dst to the size of the rectangle you want the entire image to be scaled into.
EDIT:
Here's a possible implementation for drawing the bitmaps in squares across on the canvas. Assumes the bitmaps are in a 2-dimensional array (e.g., Bitmap bitmapArray[][];) and that the canvas is square so the square bitmap aspect ratio is not distorted.
private static final int NUMBER_OF_VERTICAL_SQUARES = 5;
private static final int NUMBER_OF_HORIZONTAL_SQUARES = 5;
...
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
int squareWidth = canvasWidth / NUMBER_OF_HORIZONTAL_SQUARES;
int squareHeight = canvasHeight / NUMBER_OF_VERTICAL_SQUARES;
Rect destinationRect = new Rect();
int xOffset;
int yOffset;
// Set the destination rectangle size
destinationRect.set(0, 0, squareWidth, squareHeight);
for (int horizontalPosition = 0; horizontalPosition < NUMBER_OF_HORIZONTAL_SQUARES; horizontalPosition++){
xOffset = horizontalPosition * squareWidth;
for (int verticalPosition = 0; verticalPosition < NUMBER_OF_VERTICAL_SQUARES; verticalPosition++){
yOffset = verticalPosition * squareHeight;
// Set the destination rectangle offset for the canvas origin
destinationRect.offsetTo(xOffset, yOffset);
// Draw the bitmap into the destination rectangle on the canvas
canvas.drawBitmap(bitmapArray[horizontalPosition][verticalPosition], null, destinationRect, null);
}
}
Try the following code :
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawBitmap(bitmap, x, y, paint);
==================
You could also just reference this answer.

How to draw a filled square box in Java that is exactly in the center of an applet window?

How do you draw a filled square box in Java that is exactly in the center of an applet window? and when resizing the window, it is centered horizontally and vertically within the applet window? I want it to adapt to the vertical height of the screen but stay square even as the horizontal width edges. If the window is resized to be too narrow, then the sides might cut off?
Here's an example of a panel that will either make a 30px square in the middle, or resize with the panel. Perhaps it can give you enough to make progress.
private class MyPanel extends JPanel{
int height = 30;//30 pixels high.
int width = 30;//30 pixels wide.
boolean resize = true;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int verticalCenter = this.getHeight()/2;
int horizontalCenter = this.getWidth()/2;
if(!resize){
int topLeftSquareCornerY = verticalCenter - (height/2);
int topLeftSquareCornerX = horizontalCenter - (width/2);
g.setColor(Color.BLUE);
g.drawRect(topLeftSquareCornerX, topLeftSquareCornerY, width, height);
}else{
g.setColor(Color.GREEN);
g.drawRect(15,15,(this.getWidth()-30), this.getHeight()-30);
}
}
}
Sounds like your basic problem is figuring out how to place a given rectangle correctly. You need to have the middle of the screen in the middle of the rectangle.
The distance from the middle of the rectangle to its sides are half the height and length respectively.
So x1, x2 = middle_x ± width/2, and y1, y2 = middle_y ± height/2.
I'm guessing you want to draw a square with a fixed size that stays in the center of the panel as the panel is resized. One approach to such problems is start from the end and work backward. You know about fillRect(), so write down what you need to "fill on the blanks" required by that method. Call the center coordinates x and y. The top corner would be half of size up, and the left corner would be half of size to the left; the square's width and height would be just size:
g.fillRect(left, top, width, height);
g.fillRect(x - size/2, y - size/2, size, size);
Now go back and figure out that x and y are half the panel's width and height, respectively:
int x = getWidth() / 2;
int y = getHeight() / 2;
Now put it all together in your paintComponent() method.

Categories