Differences between drawing an Ellipse in Android and Java - java

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.

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 to adjust sin (x) graph coordinates in java to fill only a portion of the window

so currently I am working on a project for class in which I am required to draw the sin(x) function via the drawLine() method. Currently this is the loop I am using to achieve this:
int xShift = getWidth() / 50;
int xShift2 = getWidth() / 100;
int yShift = getHeight() / 10;
int yShift2 = getHeight() / 17;
int xStart = xShift;
int xEnd = xShift;
int yStart = getHeight() / 2;
int yEnd = getHeight() / 2;
int scale = getHeight() / 2;
for (double i = Math.PI / 32; i <= Math.PI * 2; i+= Math.PI / 32){
xEnd += getWidth() / 64;
yEnd = scale - ((int) Math.round(Math.sin(i) * scale));
g.drawLine(xStart, yStart, xEnd, yEnd);
xStart = xEnd;
yStart = yEnd;
}
This outputs something that looks like this:
What I want to change is that the graph will be within the constraints of the dashed blue lines, and it also will go to the end of the grey line where the 2pi marker is (marked by the blue arrow). How can I go about making these changes?
Note: this is what it looks like when I maximize the window:
For some reason the sin graph goes beyond the bounds I want it to.
Thank you for your time and I appreciate any help you can offer.
First, let's see what you are doing with your calculation of Y.
yEnd = scale - ((int) Math.round(Math.sin(i) * scale));
Since the actual sine function goes between -1 and 1, it means that your yEnd will go between scale - scale and scale + scale. This means it will go between 0 (the edge of the window), and 2×scale. Since your scale is half the height of the window, 2×scale means the full height of the window. Again - the edge of the window.
First, think what happens if your scale is smaller. If instead of height/2, the scale is (height/2 - 10), then 2×scale will be the window height - 20. That's more or less the amplitude you want - but it still scale - scale, so it still starts from the edge (try it!). Reducing scale further will lower the amplitude, but still, you will start from the edge.
To prevent that, you should change the formula. It shouldn't be adding the sine to scale. Think: when the sine is -1, you want it to be at the greatest distance from the middle of the window. When it's +1, you want the line to be at the greatest distance, again, from the middle of the window. Now that you changed scale, it's no longer half the height of the window, so you shouldn't use it for your base line.
You should have one parameter that says "what is the base height of my graph", and one parameter that says "what is the maximum amplitude of my graph". The two parameters shouldn't be the same:
int baseHeight = getHeight() / 2;
int amplitude = getHeight() / 2 - getHeight() / 50;
...
// In the loop
yEnd = baseHeight + ((int) Math.round(Math.sin(i) * amplitude));
Play around with these parameters and you'll see how they affect the way your graph is drawn.
Now as for your X. You want to do 64 steps that represent 2π. But if you want the graph to be less wide than the window, the step size cannot be getWidth() / 64. You start width/50 from the edge, add 63 * getWidth() / 64 to that. Since width/50 is more than width/64, you will be drawing more than your width.
So you need to calculate the actual final width of the graph: It's supposed to be the total width, excluding the right and the left margins. So getWidth() - 2 * getWidth()/50 is the actual width, and each step should be 1/64 of that.
int step = ( getwidth() - getWidth() / 25 ) / 64;
...
// In the loop
xEnd += step;

Creating oval/circle inside a rectangle

I am trying to create an oval/circle inside a rectangle. I am trying to do this on canvas for a bitmap image. Here is my code:
int x = (int) (midpoint.x*xRatio);
int y = (int) (midpoint.y*yRatio);
int radius = (int) (distance/2);
int left = x - radius;
int right = x + radius;
int top = y - radius;
canvas.drawRect(left, top, right, bottom, paint);
Now i want to create an oval/circle inside this rectangle. I tried this and been trying for hours cant get it to work:
RectF ovalBounds = new RectF();
//ovalBounds.set(x, y, (right - left)/2, (bottom-top)/2);
ovalBounds.set(x, y-radius, radius * 2, radius * 2);
canvas.drawOval(ovalBounds, paint);
Can someone please help me figure this out?
Here is visual to help what i am trying to achieve:
You should use the same bounds than you used for drawing the rectangle:
RectF rect = new RectF(left, top, right, bottom);
canvas.drawRect(rect, paint);
canvas.drawOval(rect, paint);

Work out the maximum size of an image?

I am creating a little game in Java and I have an image which gets rotated.
As you can see in the two images below, there is a giant ship which slowly rotates in the game, but when it gets to a certain point it gets cut off (due to its own little BufferedImage).
Heres my rendering code:
public void drawImageRotated(BufferedImage img, double x, double y, double scale, double angle) {
x -= xScroll;
y -= yScroll;
BufferedImage image = new BufferedImage((int)(img.getWidth() * 1.5D), (int)(img.getHeight() * 1.5D), 2);
Graphics2D g = (Graphics2D)image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.rotate(Math.toRadians(angle), image.getWidth() / 2, image.getHeight() / 2);
g.drawImage(img, image.getWidth() / 2 - img.getWidth() / 2, image.getHeight() / 2 - image.getHeight() / 2, null);
g2d.drawImage(image, (int)(x-image.getWidth()*scale/2), (int)(y-image.getHeight()*scale/2), (int)(image.getWidth()*scale), (int)(image.getHeight()*scale), null);
g.dispose();
}
Back to the matter at hand, how can i work out the maximum x and y size of an image during rotation so I can compensate with my buffered images size?
If you have a basically rectangular image which is rotated around its center, the maximum width and height during rotation will be when a diagonal of the image rectangle is horizontal or vertical. This diagonal distance could be computed with the Pythagorean Theorem and used for the width and height of the BufferedImage.
int size = (int) Math.sqrt((img.getWidth() * img.getWidth()) + (img.getHeight() * img.getHeight()));
BufferedImage image = new BufferedImage(size, size, 2);
// The rest of your code as before
how can i work out the maximum x and y size of an image during rotation so I can compensate with my buffered images size?
double sin = Math.abs(Math.sin(angle));
double cos = Math.abs(Math.cos(angle));
int w = image.getWidth();
int h = image.getHeight();
int neww = (int)Math.floor(w*cos+h*sin);
int newh = (int)Math.floor(h*cos+w*sin);
The above code was taken from this example: Java(SWING) working with Rotation
An alternative is to rotate the actual Graphics object, draw the image, and restore the rotation:
AffineTransform old = g2d.getTransform();
g2d.rotate(Math.toRadians(angle), x + image.getWidth() / 2, y + image.getWidth() / 2);
g2d.drawImage(image, x, y, null);
g2d.setTransform(old);
Let's consider width being the width of the original image, height its original height and angle the rotation angle value in radians.
According to my calculations, the size of the rotated image is something like this:
rotatedWidth = Math.cos(angle) * width + Math.sin(angle) * height;
rotatedHeight = Math.sin(angle) * width + Math.cos(angle) * height;
You may also need to take a look at this thread as well, as it may help.

How to approximate an ellipse to fill a given rectangle, using bezier curves?

I tried the code below, which draws a good approximation of a circle if the rectangle's width is the same as its height; but it doesn't draw a great oval, the "corners" are very pointed. Any suggestions?
float width = rect.width();
float height = rect.height();
float centerX = rect.width() / 2;
float centerY = rect.height() / 2;
float diameter = Math.min(width, height);
float length = (float) (0.5522847498 * diameter/2);
path.moveTo(0, centerY);
path.cubicTo(0, centerY - length, 0, centerX - length, 0, centerX, 0);
path.cubicTo(centerX + length, 0, width, centerY - length, height, centerY);
path.cubicTo(width, centerY + length, centerX + length, height, centerX, height);
path.cubicTo(centerX - length, height, 0, centerY + length, 0, centerY);
You should scale length according to which axis it's on, so that the distance from each arc endpoint to the adjacent control points is (not fixed but) a fixed fraction of the axis you're moving parallel to at that point.
If it's a true rectangle, with right angles, then it should be easy. The major and minor axes of the ellipse equal the lengths of the sides of the rectangle, and the center of the ellipse is located at the intersection of the rectangle's diagonals.
I don't know how to express it as Bezier splines off the top of my head, but the classic equation for an ellipse should be easy enough to write, as long as you transform to the appropriate coordinate system first (e.g. x-axis along the major axis of the rectangle/ellipse, y-axis along the minor axis of the rectangle/ellipse).

Categories