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.
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
Here i'm load texture and make textureregion and TiledDrawable instances:
textures = new Texture(Gdx.files.internal("somefile.png"));
bg_grass_region = new TextureRegion(textures, 631, 175, 116, 662);
bg_grass_tiled = new TiledDrawable(bg_grass_sprite);
and inside render method of Screen instance i draw it like this:
batch.begin();
bg_grass_tiled.draw(batch, 0, 0, bg_grass_sprite.getWidth(), bg_grass_sprite.getHeight()*3);
batch.end();
The problem is that bg_grass_region is TextureRegion instance, and it complitly won't to scale...
So, i have a one simple question: How to scale/resize bg_grass_tiled or bg_grass_region objects?
TiledDrawable draws a TextureRegion repeatedly to fill the area, instead of stretching it. Implemented by TransformDrawable, however It not support scaling and rotation.
so you can't use below method of TiledDrawable, it throws UnsupportedOperationException
draw (Batch batch, float x, float y, float originX, float originY, float width, float height, float scaleX,float scaleY, float rotation)
Why don't you use draw method of SpriteBatch with TextureRegion.
draw (TextureRegion region, float x, float y, float originX, float originY, float width, float height,float scaleX, float scaleY, float rotation);
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...
I am wondering how to rotate things I have already drawn (like lines) in Java drawing Panel (not in JPanel).
I am trying to rotate a triangle i created by connecting 3 lines:
g.drawLine(size, size, 2*size, size);
g.drawLine(size, size,size+size/2, size+(int)(size/2 * Math.sqrt(3)));
g.drawLine(2*size, size,size+size/2, size+(int)(size/2 * Math.sqrt(3)));
How do I rotate that?
If you want to rotate a point like that, then you could:
double startX; // <------------|
double startY; // <------------|
double endX; // <------------|
double endY; // <-define these
double angle; // the angle of rotation in degrees
To draw the original line
g.setColor(Color.BLACK);
g.drawLine(startX, startY, endX, endY); //this is the original line
double length = Math.pow(Math.pow(startX-endX,2)+Math.pow(startY-endY,2),0.5);
double xChange = length * cos(Math.toRadians(angle));
double yChange = length * sin(Math.toRadians(angle));
To draw the new, rotated line
g.setColor(Color.GRAY);
g.fillRect(0,0,1000,1000); //paint over it
g.setColor(Color.BLACK);
g.drawLine(startX, startY, endX + xChange, endY + yChange);
Use graphics2D and Polygons
Graphics2D g2 = (Graphics2D) g;
int x2Points[] = {0, 100, 0, 100}; //these are the X coordinates
int y2Points[] = {0, 50, 50, 0}; //these are the Y coordinates
GeneralPath polyline =
new GeneralPath(GeneralPath.WIND_EVEN_ODD, x2Points.length);
polyline.moveTo (x2Points[0], y2Points[0]);
for (int index = 1; index < x2Points.length; index++) {
polyline.lineTo(x2Points[index], y2Points[index]);
};
g2.draw(polyline);
If you want to rotate it, just do it over but add, before
g2.rotate(Math.toRadians(angle), centerX, centerY);
Where "angle" is the amount you want to rotate it and (centerX, centerY) are the coordinates of the point you want to rotate it around.
I hope this helps
In Java for some reason the Ellipse2D.Double uses the parameters (height, width, x, y) where as when I create a RectF in Android the parameters are (left, top, right, bottom) so I'm a bit confused on adjusting to the differences.
If a create an Ellipse in Java and use the following:
//Ellipse2D.Double(height, width, x, y)
x = 100;
y = 120;
centerX = getWidth() / 2;
centerY = getHeight() / 2;
//Ellipse2D.Double(100, 120, (centerX - 100) * 2, (centerY - 120) * 2);
new Ellipse2D.Double(x, y, (centerX - x) * 2, (centerY - y) * 2);
Would this be equivalent for Android:
//RectF(left, top, right, bottom)
x = 100;
y = 120;
centerX = getWidth() / 2;
centerY = getHeight() / 2;
new RectF((centerX - 100) * 2, (centerY - 120) * 2), 120 - ((centerX - 100) * 2), 100 - ((centerY -120) * 2);
//canvas.drawOval(myRectF, paint);
I'm not quite sure if they are equivalent, and am wondering if I am calculating it correctly?
Alternatively, can one override the RectF to make it simliar to how Ellipse2D? Ie. change the parameters to work with height and width rather than right and bottom?
For the override part, I don't thing it would be a good idea since RectF isn't only used for ellipses.
you can easily write a method that draw the Oval by passing the data the way you prefer...
something like:
public RectF myOval(float width, float height, float x, float y){
float halfW = width / 2;
float halfH = height / 2;
return new RectF(x - halfW, y - halfH, x + halfW, y + halfH);
}
canvas.drawOval(myOval(width, height, x, y), paint);
To keep in the x, y, width, height thinking, you can construct a utility function to build a RectF with the coordinates in the order you like to think of them:
public static RectF buildRectF(double x, double y, double width, double height) {
// r(l, t, r, b)
RectF rectf = new RectF(x - width / 2, y - height / 2, x + width / 2, y + height / 2);
return rectf;
}
It is unclear what you are trying to do with the code sample you have.
Ellipse2D.Double takes 4 parameters: x, y, width and height.
It looks like you are setting width to be (centerX - x) * 2; this will ensure that the width is twice the distance from the center of the component your code resides in to the point (100, 120), if the center is to the right of the point (100, 120). If your component gets too small, though, you will assign a negative width, which could be awkward.
Also, you are using hardcoded values in your RectF example, and combining 120 (the y?) and 100 (the x?) in the same arguments to RectF, which is most likely not what you want to do.
I'd suggest drawing a picture on a piece of paper, label the coordinates with the values you think they should be, then write your code. You should be able to more clearly see what your top left bottom and right (or x, y, width and height) values should be.