Problem with getting Circle round when increasing radius with JavaFX - java

When creating a Circle object with JavaFX and is using Graphic Context to stroke the Oval I want it to expand outside of the first created oval. So it will be larger than the last one and go around the first if that makes sense to you.
Here is a picture of what it is now:
Here is a picture of what I would like it to do. As well as the fillOval method too:
Canvas canvas = new Canvas(400, 200);
GraphicsContext gc;
gc = canvas.getGraphicsContext2D();
gc.setLineWidth(1);
Circle c = new Circle();
canvas.setOnMousePressed(e ->
{
c.setCenterX(e.getX());
c.setCenterY(e.getY());
});
canvas.setOnMouseDragged(e ->
{
c.setRadius((Math.abs(e.getX() - c.getCenterX()) + Math.abs(e.getY() - c.getCenterY())) / 2);
gc.strokeOval(c.getCenterX(), c.getCenterY(), c.getRadius(), c.getRadius());
}
});
For some reason it begins at the left corner. I cant understand why it does that. It doesnt make any sense to me.

It seems to me your assumption on how strokeOval is defined is wrong.
public void strokeOval​(double x, double y, double w, double h)
The parameters define a bounding rectangle and not, as you assume, a center and a radius. Just have a look at the documentation for more details.

Related

Issues with coordinates

I'm working on something that involves clicking specific points on a buffered image in a JPanel. I had issues with this earlier in the project (affine transform translation not working properly), but nothing I found fixed it so I decided I would come back to it later.
I'm not entirely sure how to trouble shoot it since I'm a novice, but I think it's reading my y coordinates too low. I made a mouse input listener that tracks the number of times the user has clicked and gets the mouse pointer's location for functions I haven't made yet. For testing I have it output the coordinates and number of clicks then make a circle centered where the mouse clicks.
#Override
public void mouseClicked(MouseEvent e) {
Point mouseCursor = MouseInfo.getPointerInfo().getLocation();
panel.drawCenteredCircle(mouseCursor.getX(), mouseCursor.getY(), 100);
System.out.println(String.valueOf(mouseCursor));
System.out.println(String.valueOf(clickCount));
clickCount++;
}
Here is drawCenteredCircle in my custom panel class:
public void drawCenteredCircle(double x, double y, int r) {
imgG2 = image.createGraphics();
imgG2.setPaint(Color.RED);
x = (x-r/2.0);
y = (y-r/2.0);
imgG2.fillOval((int)Math.round(x), (int)Math.round(y), r, r);
this.repaint();
imgG2.dispose();
}
I tried taking a screenshot to show what happens, but the circle properly centers on the x coordinate, but not the y coordinate. Instead it draws the circle with the pointer at the top center edge.
I overrided the paintComponent of my JPanel to implement a zoom feature:
#Override
protected void paintComponent(Graphics g) {
//Implimenting zoom
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g.create();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
/*Supposed to counter the movement from the scale, not working properly
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
double x = (w - scale * imageWidth)/2;
double y = (h - scale * imageHeight)/2;*/
AffineTransform at = new AffineTransform()/*.getTranslateInstance(x, y) */;
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
//g2.dispose(); I was told to put this, but I'm not sure if it's necessary or what it does entirely
}
My confused notes are because I got this code from an example someone made and, as I said earlier, the affine translation wasn't working (I took the actual translation out). They're irrelevant to the question.
The reason I put this is because I initially had code that was meant to fit the image to the screen/frame depending if it was fullscreen or not:
int x = image.getWidth();
int y = image.getHeight();
double frameW = frame.getBounds().getWidth();
double frameH = frame.getBounds().getHeight();
//Rectangle winSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
double screenW = Toolkit.getDefaultToolkit().getScreenSize().getWidth();
double screenH = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
if (!isFullScreen) {
if (x/y > frameW/frameH) {
scale = frameW/x;
} else {
scale = frameH/y;
}
} else {
if (x/y > screenW/screenH) {
scale = screenW/x;
} else {
scale = screenH/y;
}
}
It uses my zoom function which scales the image with the double "scale." I noticed that when I zoomed in or out, it would change where the dots would appear relative to the pointer. It wasn't until I removed the code for the image to start fitted to the window and had it start at 100% that I received the result of the pointer being at the top center of the circle.
I also tried removing the part that's supposed to center the circle and the result was the pointer being on the left side and having a gap between it and the top of the circle.
Sorry if this is too much stuff. I'm pretty novice and learned just as much about java (the only coding language I know) working on this project as I knew when I first started it. I'm not sure what information I have that could be helpful in this, so I just threw in everything I thought could help. I appreciate any help, even irrelevant to my question.

How to flip entire awt.Canvas vertically

I am sitting with my son, trying to implement a school homework. The task is to write a program that draws X and Y axis and functions, e.g. Sinus or x² into a awt.Canvas. The issue we are struggeling with is that the root, Point(0,0) of the Canvas is designed to be in the upper left corner. The cartesian coordinate system that we have to have, has the origin in the lower left corner. So we tried to apply a AffineTransform and translate in the paint method of the Canvas, which in essence works but has two issues:
1st, for whatever reason the related translation doesn't really moves the origin to the bottom but about 100 pixels to high (see image).
When we put in the below code an additional offset of about 100 pixels with tx.translate(0, -(getHeight()+100)); it looks about right.Same issue seems to be true on the right side. There is also unintended free space. We colored the background of the containing Frame in black and the Canvas in grey to exclude an artefact between these two containers. But doesn't seem to be the case.
2nd, and that concerns us more, is the side effect that all text, when e.g. adding values to the axes will also be fliped, as you see at our debug info in the plotAxes method.
Here is what we have done so far..
public class PlotterView extends Canvas {
protected int MINWIDTH = 500;
protected int MINHEIGHT = 400;
Point[][] lines;
public PlotterView() {
Dimension dim = new Dimension(MINWIDTH, MINHEIGHT);
setPreferredSize(dim);
setBackground(Color.LIGHT_GRAY);
}
protected void plotAxes(Graphics2D g) {
Color defaultColor = g.getColor(); // save to restore defaults in the end
int originX = 5; // x origin of both axes - shift right
int originY = 5; // y origin of both axis - shift up
// Debug info to compare
g.setColor(Color.BLACK);
g.drawString("X: " + originX + "; Y: " + originY, originX, originY);
// X-Axis
g.setColor(Color.RED);
g.drawLine(originX, originY, MINWIDTH-20, originY);
g.drawLine(MINWIDTH-20, originY, MINWIDTH-30, originY-5);
g.drawLine(MINWIDTH-20, originY, MINWIDTH-30, originY+5);
// Y-Axis
g.setColor(Color.BLUE);
g.drawLine(originX, originY, originX, MINHEIGHT-20);
g.drawLine(originX, MINHEIGHT-20, originX-5, MINHEIGHT-30);
g.drawLine(originX, MINHEIGHT-20, originX+5, MINHEIGHT-30);
// Restore defaults
g.setColor(defaultColor);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
tx.translate(0, -getHeight());
g2.setTransform(tx);
plotAxes(g2);
}
}
One alternative would be to implement a method that "adjusts" every x-value from upper left to lower left, but that feels like a kind of botch job.
How to solve this right? Thank you in advance
Thank you for the feedback. I figured out that issue number 1 is born by using the AffineTransform. If I apply the scale and translate on the g2 directly the issue disappears.
Issue number 2 is a conflict of interest. We couldn't manage to find and apply the "three magic lines of code" (or whatever number would be required) to from thereon programm in a cartesian coordinate system. Instead we are converting all y-values into this top-level-origin coordinate system. Makes the code hard to read, but with the help debugging we managed.

Changing origin for circle in JPanel

I'm using the JPanel and JFrame to animate an example of something called Circle Packing, which essentially is just filling an object with continuously growing circles to fill the object with circles of different sizes.
I've been able to animate an arraylist of circle objects, but they grow in a way that is not desirable for my project. A circle growing, according to me, is a fixed point P from which a circle with radius R around it is created, and it expands solely by the R increasing. JPanel does not act this way. It changes the x and y positions as well, which I do not understand.
This is my repaint:
public void repaint(Graphics g) {
g.setColor(new Color(125, 0, 100));
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(new Color(255, 165, 0));
Circle myCircle = new Circle(rand.nextInt(500), rand.nextInt(500), 1);
circles.add(myCircle);
for (Circle c : circles) {
int newx = (int) c.getX();
int newy = (int) c.getY();
int newsize = (int) c.getSize();
c.grow();
c.growth(); // These are the growing functions
c.Edges();
g.drawOval(newx, newy, newsize, newsize);
}
}
and my grow, growth and Edges are to check for cases where the circles touches the sides of the screen.
Please edit my question to a more appropriate title if necessary, I'm still new to S.O.
A circle growing, according to me, is a fixed point P from which a circle with radius R around it is created, and it expands solely by the R increasing
Maybe, but you didn't write the Graphics API. So your code needs to work based on the methods of the API.
It changes the x and y positions as well, which I do not understand.
Read the API for the drawOval(...) method to understand how it works. Don't assume how it works.
The method works by specifying the top/left x/y point, not the center point.
So if the radius increases, the x/y values must decrease if you want the center point to remain constant. So you need to fix your grow logic.
I'm using the JPanel and JFrame to animate an example of something called Circle Packing,
Also, don't generate random values in a painting method. You can't control when the component will be repainted so the values can change unexpectedly. That is don't change the state of an object in a painting method.
So you need a method to "grow" each circle (which is separate from your painting code). This method will adjust the radius and x/y location of each object in the Array. Then you invoke repaint() on the panel. The painting code will then just iterate through the Array and paint the current state of each object in the array.

Move a Rectangle from point to point in a ramp with changing angle

So I have this program to test the possibility of an object to slide down in a ramp given its friction, object mass and ramp angle. However I need to animate the box if the force is positive. Just a simple animation moving the box from that point to the end of the ramp. But I can't. Please help
private void drawTransform(Graphics g, double modifier) {
// redtowhite = new GradientPaint(0,0,color.RED,100, 0,color.WHITE);
Rectangle rect = new Rectangle(130,350, 350, 15);
Rectangle box = new Rectangle((int) (rect.getX()+300), 300, 50, 50);
AffineTransform at = new AffineTransform();
at.rotate(-Math.toRadians(modifier), rect.getX(), rect.getY() + rect.height);
// Transform the shape and draw it to screen
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.DARK_GRAY);
// g2d.fillRect(0, 0, 350, 600);
g2d.fill(at.createTransformedShape(rect));
g2d.draw(at.createTransformedShape(box));
}
Screenshot:
If all you want to do is move the box, this can be done by simply updating it's X position. You should be able to manipulate the rectangle's X position directly using something like "box.x++". Alternatively you could create a variable and reference that to provide the initial X co-ordinate, then updating that variable will "move" the box. One issue is this will only move the box along the X axis, hence you will also need some kind of constant downward force acting as gravity. This is easy to achieve, just minus the box's Y position value when it is not colliding with the ground, or your ramp.
Another approach is velocity based movement using vectors, however you mentioned that the animation should be simple. If you do want a smoother animation velocity based movement will provide this but you will need to perform a little research first.

JavaFX GraphicsContext clearRect doesn't work with clip mask

The documentation for the clearRect method of GraphicsContext states that it uses the current clip, but this isn't currently working for me. Consider:
GraphicsContext context = canvas.getGraphicsContext2D();
context.beginPath();
context.rect(0,0,100,100); //Set the current path to a rectangle
context.stroke(); //Highlights where the current path is
context.clip(); //Intersect current clip with rectangle
context.fillOval(80, 80, 40, 40); //This correctly draws the oval clipped
context.clearRect(0,0,100,100); //This does nothing at all
The above code sets the clip mask correctly, as evidenced by the fact that fillOval works correctly, however clearRect does nothing (although it works normally without the context.clip()). Why is this?
(Note that I specifically need the clip mask to be working, as later I plan on setting it to specific shapes to erase in non-rectangular shapes.)
-- Edit --
To be clear, clearRect does literally nothing, not even erase the oval. I realise that it won't erase the stroked rectangle but that's not what I'm concerned about.
-- Edit 2 --
Updating to the latest JDK has partially fixed the issue. The above code now works correctly. However, using a non-rectangular clip mask still has the same problem. e.g.
GraphicsContext context = canvas.getGraphicsContext2D();
context.beginPath();
context.arc(50, 50, 40, 40, 0, 360); // Make a circular clip mask
context.closePath();
context.clip();
context.fillRect(0, 0, 200, 200); //Draw a circle clipped correctly, shows clip mask is working
context.clearRect(0, 0, 200, 200); //Does nothing
I realise I could use save and restore to get a rectangular clip mask back, and then clearRect would work. However I want to be able to erase in non-rectangular shapes.
Full code for reproducing this is (created by making a new JavaFX project in eclipse and adding the above lines):
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
Canvas canvas = new Canvas(500, 500);
root.getChildren().add(canvas);
GraphicsContext context = canvas.getGraphicsContext2D();
context.beginPath();
context.arc(50, 50, 40, 40, 0, 360);
context.closePath();
context.clip();
context.fillRect(0, 0, 200, 200);
context.clearRect(0, 0, 200, 200);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
This should show a blank screen, but the circle is not being cleared.
As per Edit 2, this behaviour seems to be a bug. In summary, clearRect has no effect when a non-rectangular clip is set. (If it isn't working for you even with a rectangular clip, update to the latest JDK.) I've filed a bug report for this.
While i agree that this should be fixed, but am facing the fact that it just is not, i have come up with a solution that meets my specific needs at least.
It is a method that clears an area based on an SVGPath using the GraphicsContext.
private void clearPath(GraphicsContext gc, SVGPath path) {
int xstart = (int) path.getLayoutX();
int xend = (int) (xstart + path.getLayoutBounds().getMaxX());
int ystart = (int) path.getLayoutY();
int yend = (int) (ystart + path.getLayoutBounds().getMaxY());
PixelWriter pw = gc.getPixelWriter();
for (int x = xstart; x <= xend; x++) {
for (int y = ystart; y <= yend; y++) {
if(path.contains(new Point2D(x, y))) {
pw.setColor(x, y, Color.TRANSPARENT);
}
}
}
}
The code works just fine on Windows 7 with JavaFX 8u40.
What you should do is to provide a MCVE. Nobody can possibly guess what else you've done with the GraphicsContext. There may be a save and restore missing and what not. By the way, your code lacks a closePath().
Answer before your edit:
The problem you are facing is the way JavaFX draws the lines. Check out the documentation of the Node class:
At the device pixel level, integer coordinates map onto the corners
and cracks between the pixels and the centers of the pixels appear at
the midpoints between integer pixel locations. Because all coordinate
values are specified with floating point numbers, coordinates can
precisely point to these corners (when the floating point values have
exact integer values) or to any location on the pixel. For example, a
coordinate of (0.5, 0.5) would point to the center of the upper left
pixel on the Stage. Similarly, a rectangle at (0, 0) with dimensions
of 10 by 10 would span from the upper left corner of the upper left
pixel on the Stage to the lower right corner of the 10th pixel on the
10th scanline. The pixel center of the last pixel inside that
rectangle would be at the coordinates (9.5, 9.5).
That's why you have a thin line on the right and at the bottom. The lines aren't crisp.
I suggest you move the rectangle to the center and also use the fill method to make this more visible for you.
Your code without clearRect:
Your code with clearRect:

Categories

Resources