I have a problem to manipulate an Arc shape in javafx with mouse drag events
I have an Arc defined with the following paramters:
radiusX and radiusY :horizontal and vertical radius of the full ellipse of which this arc is a partial section
centerX,centerY : center point of the Arc
startAngle: starting angle of the arc (relative to the horizontal axis)
length: angular extent of the arc in degrees
So what i need is to make the start angle of the arc "follow" the mouse movement while dragging:
when the user press on the starting point of the Arc (located on the startAngle) and while dragging the mouse i need to compute the new starting angle formed by the horizontal axis and the line from the center to the mouse position
Basically the problem is to compute the start angle of the arc given a point (the mouse position) and the other given parameters (center, major and minor axis)
What i'm doing actuyally is calculating the angle with the Math.atan2 function
newStartAngle = atan2(xMouse, yMouse) (assume center x, y at 0,0)
but it works only if the arc is circular (radiusX = radiusY)
Another way to put it is : i need the start point of the arc to be always on the line from the center to the mouse position (so i need to continually update the start angle in order to make it follow the mouse rotation movement). (i hope i've made it a little clear)
Here is the complete source of a sample
import javafx.stage.Stage;
...
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Group designer = createDesigner();
designer.setLayoutX(100);
designer.setLayoutY(200);
pane.getChildren().add(designer);
Scene sc = new Scene(pane, 600, 600);
primaryStage.setScene(sc);
primaryStage.show();
}
public static final double RX = 100;
public static final double RY = 50;
public static final double S_ANGLE = 45;
public static final double ARC_LENGTH = 90;
private Arc arc;
private Circle handle;
private Line connection;
double xMouse,yMouse;
public Group createDesigner() {
arc = new Arc();
arc.setRadiusX(RX);
arc.setRadiusY(RY);
arc.setStartAngle(S_ANGLE);
arc.setLength(ARC_LENGTH);
arc.setFill(Color.LIGHTBLUE);
arc.setType(ArcType.ROUND);
handle = new Circle();
handle.setRadius(5);
handle.setStroke(Color.BLACK);
handle.setFill(Color.TRANSPARENT);
handle.setCenterX(
RX * Math.cos(Math.toRadians(S_ANGLE))
);
handle.setCenterY(
-RY * Math.cos(Math.toRadians(S_ANGLE))
);
connection = new Line();
connection.startXProperty().bind(arc.centerXProperty());
connection.startYProperty().bind(arc.centerYProperty());
connection.endXProperty().bind(handle.centerXProperty());
connection.endYProperty().bind(handle.centerYProperty());
handle.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
xMouse = event.getX();
yMouse = event.getY();
handle.setCenterX(xMouse);
handle.setCenterY(yMouse);
double angleInRadians = Math.atan2(-yMouse, xMouse);
arc.setStartAngle(Math.toDegrees(angleInRadians));
}
});
return new Group(arc, connection, handle);
}
public static void main(String[] args) {
launch(args);
}
}
Thank you in advance
well i solved it, suffice to divide xMouse and yMouse by major and minor axe to make it work
newStartAngle = Math.atan2(-yMouse/radiusY, xMouse/radiusX)
//assume center x, y at 0,0
Related
I am trying to make a rectangle object to go around a circle, with the rectangle always tangent to the circle where it is going around. I have the code that will make it go around the circle, but I don't see how to make it tangent to it. This is how it looks so far.
I am using an animation timer, since I don't know the full path that the rectangle will follow as it can change if I it finds something blocking it.
I can make the rectangle go around the circle in a smooth way, but I don't know how to make the rectangle tangent to it.
public void moveInCircle(double radius)
{
double newX = getX() + (radius * Math.cos(Math.toDegrees(angle)));
double newY = getY() + (radius * Math.sin(Math.toDegrees(angle)));
vehicle.setTranslateX(newX);
vehicle.setTranslateY(newY);
}
I know that the tangent will be the adjacent side(x) divided by the opposite side (y), but I don't understand how to incorporate it.
I recommend using a Rotate transform. This way you only need to set the initial position and the pivot point and can restrict the updates to the Rotate.angle property.
The following example uses a Timeline to animate the property, but this could easily be done from the moveCircle method by using rotate.setAngle(angleDegrees);:
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
root.setMinSize(500, 500);
final double radius = 150;
final double centerX = 250;
final double centerY = 250;
final double height = 40;
Circle circle = new Circle(centerX, centerY, radius, null);
circle.setStroke(Color.BLACK);
// rect starts at the rightmost point of the circle touching it with the left midpoint
Rectangle rect = new Rectangle(centerX + radius, centerY - height / 2, 10, height);
rect.setFill(Color.RED);
Rotate rotate = new Rotate(0, centerX, centerY); // pivot point matches center of circle
rect.getTransforms().add(rotate);
// animate one rotation per 5 sec
Timeline animation = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(rotate.angleProperty(), 0d)),
new KeyFrame(Duration.seconds(5), new KeyValue(rotate.angleProperty(), 360d)));
animation.setCycleCount(Animation.INDEFINITE);
animation.play();
root.getChildren().addAll(circle, rect);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
Btw: the following part of your code seems odd
double newX = getX() + (radius * Math.cos(Math.toDegrees(angle)));
double newY = getY() + (radius * Math.sin(Math.toDegrees(angle)));
Math.sin and Math.cos expect radians, not degree. You either need to use toRadians or don't require a conversion...
I want to know how I can make an interaction when rectangle collapses with a circle . Like some kind of action that happens when circle and rectangle collapses. There is also a problem that circle can go out of frame, which I don't know how to limit it's movement. I'm not even sure if it's possible to do what I want this way.This is supposed to be a game where I need to dodge all rectangles and this is best I could do. Thanks it advance :)
public class Java2 extends Application {
public static final int KRUG_WIDTH = 10;
public static final int PANEL_WIDTH = 600;
public static final int PANEL_HEIGHT = 600;
private int mX = (PANEL_WIDTH - KRUG_WIDTH) / 2;
private int mY = (PANEL_HEIGHT - KRUG_WIDTH) / 2;
Random ran = new Random();
#Override
public void start(Stage primaryStage) {
Rectangle rekt = new Rectangle(20, 20);
Rectangle rekt1 = new Rectangle(20, 20);
Circle r1 = new Circle(mX,mY,KRUG_WIDTH);
Pane root = new Pane(); //PANE
r1.setFill(Color.WHITE);
r1.setStroke(Color.BLACK);
root.getChildren().add(r1);
root.getChildren().add(rekt);
root.getChildren().add(rekt1);
Scene scene = new Scene(root, PANEL_WIDTH, PANEL_HEIGHT);
PathTransition pathTransition = new PathTransition();
Path path = new Path();
//REKT-PATH
pathTransition.setDuration(javafx.util.Duration.millis(600));
pathTransition.setPath(path);
pathTransition.setNode(rekt);
pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransition.setCycleCount(2);
pathTransition.setAutoReverse(true);
pathTransition.setOnFinished(e -> {
pathTransition.setPath(createPath());
pathTransition.play();
});
pathTransition.play();
PathTransition pathTransition1 = new PathTransition();
Path path1 = new Path();
//REKT1-PATH
pathTransition1.setDuration(javafx.util.Duration.millis(550));
pathTransition1.setPath(path1);
pathTransition1.setNode(rekt1);
pathTransition1.setOrientation(
PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransition1.setCycleCount(Timeline.INDEFINITE);
pathTransition1.setAutoReverse(true);
pathTransition1.setOnFinished(e -> {
pathTransition1.setPath(createPath());
pathTransition1.play();
});
pathTransition1.play();
r1.setOnKeyPressed(e -> {
switch (e.getCode()) {
case DOWN: r1.setCenterY(r1.getCenterY()+ 10);
break;
case UP: r1.setCenterY(r1.getCenterY()- 10);
break;
case LEFT: r1.setCenterX(r1.getCenterX() - 10);
break;
case RIGHT: r1.setCenterX(r1.getCenterX() + 10);
break;
case SPACE:
break;
default:
}
});
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
r1.requestFocus();
}
private Path createPath() {
int loc2 = ran.nextInt(600 - 300 + 1) + 300;
int loc = ran.nextInt(600 - 20 + 1) + 20;
Path path = new Path();
path.getElements().add(new MoveTo(20, 20));
path.getElements().add(new LineTo(loc, loc2));
return path;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Your approach isn't one I would take. Usually you have a timer as a game loop in which all the magic is happening, i. e. move sprites, check collision, update sprites in the UI. In JavaFX this would be an AnimationTimer.
In your code you miss several important things, among them the keyboard or mouse input. Or how you check the collision. There is e. g. an intersects method for the nodes, but that checks only the rectangular bounds of a node. You however have a circle, which makes matters more complex. However, if performance isn't an issue for your game, you could use the Shape's intersect method to create a new shape out of Rectangle and Circle and depending on the result decide whether you have a collision or not.
Some time ago I created example code about how to move sprites on the screen and check if they collide with others.
Regarding bouncing off the scene bounds in that code you simply check the scene bounds and change the movement delta to a bouncing value, i. e. if the sprite moves from left to right (dx is positive) and is at the right side, you set the delta dx to a negative value so that it moves in the opposite direction.
You can also take a look at a simple Pong game which uses a slightly modified version of that engine.
Example for shape intersection with your code:
Shape shape = Shape.intersect(rekt, r1);
boolean intersects = shape.getBoundsInLocal().getWidth() != -1;
if( intersects) {
System.out.println( "Collision");
}
That alone raises the question where you'd put the check. Normally you'd have to perform it when anything moves, i. e. in both path transitions like this
pathTransition.currentTimeProperty().addListener( e -> {
...
});
and in the circle transition. That would be 2 checks too many.
I use JPanel to draw a square on the screen.
When I use MouseDragged it works fine and goes wherever I want, almost. Each time I click on the square, the square automatically moves and the top left corner goes right under the mouse.
How should I do so that the square doesn't replace itself and stays right under the mouse ?
Thanks for any help.
Keep an account of the difference between top-left coordinates of Component which you are
moving and mousePressed location.
And when you get new position, just subtract that difference to it.
Here I have tried to explain it through coding. Let myJPanel be the component you want to
move. Then here is the MouseAdapter that can work for you. New position is stored in
newPosition variable.
new MouseAdapter(){
int diffx = 0, diffy = 0;
public void mousePressed(MouseEvent e) {
Point topLeft = myJPanel.getLocation();
Point mouseDn = e.getPoint();
diffx = mouseDn.x - topLeft.x;
diffy = mouseDn.y - topLeft.y;
}
public void mouseDragged(MouseEvent e) {
Point mouseDr = e.getPoint();
int newX = mouseDr.x - diffx;
int newY = mouseDr.y - diffy;
Point newPosition = new Point(newX, newY);
}
};
I'm creating a java program to draw an image of a box. I have most of my code finished. But, I'm having trouble figuring out a method to rotate the box by a specific number of degrees. I'm also trying to create a method to increase the size of the box by percentage and to clear my canvas of all images drawn.
This is the code I have thus far:
// My Box class
import java.awt.Rectangle;
public class Box
{
public Box(Shapes canvasRef, int leftSide, int topLeft, int theWidth, int theHeight)
{
left = leftSide;
top= topLeft;
width = theWidth;
height = theHeight;
canvas = canvasRef;
theBox = new Rectangle(left, top, width, height);
canvas.addToDisplayList(this);
show = false;
}
public void draw()
{
show = true;
theBox = new Rectangle(left, top, width, height);
canvas.boxDraw();
}
public void unDraw()
{
show = false;
theBox = new Rectangle(left, top, width, height);
canvas.boxDraw();
}
public Rectangle getBox()
{
return theBox;
}
public void moveTo(int newX, int newY)
{
left = newX;
top = newY;
draw();
}
// This is the method that I tried but doesn't do anything
public void turn(int degrees)
{
int newAngle = angle + degrees;
angle = newAngle % 60;
}
clearWorld()
{
// Clears the "canvas" upon which boxes are drawn
}
public void grow(int percentage)
{
//The box grows the specified percentage,
about the center, i.e. increase each side of the box
the percentage indicated, with the center unchanged
}
// My Driver Program
import javax.swing.JFrame;
public class DisplayList
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Joe The Box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(250, 250);
Shapes component = new Shapes();
frame.add(component);
frame.setVisible(true);
Box b1 = new Box(component, 150, 100, 30, 50);
Box b2 = new Box(component, 100, 100, 40, 60);
b1.draw();
b2.draw();
b1.turn(90);
b2.grow(100);
b1.clearWorld();
Delay.sleep(2);
b2.moveTo(10,10);
}
}
public boolean showBox()
{
return show;
}
private int left;
private int top;
private int width;
private int height;
private int angle = 0;
private Shapes canvas;
private Rectangle theBox;
private boolean show;
}
Can anyone please help me with the last three methods of my Box class?
I'm really struck on what to add?
I'm open to any suggestions.
Thanks for your time!
If you are rotating the box around (0,0) pre-multiply each coordinate, by a rotation matrix:
x=x*Math.cos(t)-y*Math.sin(t)//result of matrix multiplication.
y=x*Math.sin(t)+y*Math.cos(t)//t is the angle
Alternatively, convert to polar coordinates, r=Math.hypot(x,y) theta=Math.atan2(x,y) and add an angle to theta: theta+= rotationAngle. Then convert back to rectangular coordinates: x=r*Math.cos(theta) y=r*Math.sin(theta)
By the way you don't need the modulus; Angles greater than 360 are also ok. Oh, and all angles should be in radians. If they are in degrees, first multiply by 2pi/360 to convert them to radians.
To scale the box, multiply each coordinate by a constant scaling factor.
There are at least two ways to rotate a point around the origin, both of which are mathematically equivalent:
Use trigonometry to calculate the new (x, y) coordinates for the point.
Use linear algebra, specifically a linear transformation matrix, to represent the rotation.
I suggest that you google some keywords to learn more about either of these solutions. If you encounter specific details that you don't understand, please come back with more questions. You may also want to check out our sister site http://math.stackexchange.com where you can ask questions which are specific to the mathematics behind rotation animations.
Once you understand how to apply a rotation to a single point, you will simply need to repeat the calculations for each of the vertices of your box. This will be easiest if you encapsulate the calculations for a single point into its own method.
I am trying to have a circle appear on the screen, then when the user clicks INSIDE the circle, enable the ability for them to drag the circle where the mouse goes while it is being pressed.
This is the code i have so far, the drag works, but it is allowing the user to drag without them pressing the inside of the circle, just when anywhere on the screen is pressed.
I hope i am not too confusing
here's the code i have, please if someone could just tell me the code that needs to be corrected, it will save me anymore hours.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class DragCircle extends JFrame {
private static final long serialVersionUID = 1L;
public static int size = 400;
public static int r = 10;
private int x;
private int y;
private int cX;
private int cY;
private int dX;
private int dY;
private MouseHandler mh;
boolean isCircleClicked = false;
public static void main(String[] args) {
DragCircle c1 = new DragCircle();
c1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public DragCircle() {
super("Drag circle");
cX = r + 100;
cY = r + 100;
mh = new MouseHandler();
addMouseListener(mh);
addMouseMotionListener(mh);
setSize(size, size);
setVisible(true);
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.RED);
g.fillOval(cX, cY, r * 2, r * 2);
}
private class MouseHandler extends MouseAdapter implements
MouseMotionListener {
public void mousePressed(MouseEvent me)
{
if ((cX - me.getX()) * (cX - me.getX()) + (cY - me.getY())
* (cY - me.getY()) < r * r) {
isCircleClicked = true;
}
}
public void mouseDragged(MouseEvent me) {
if (isCircleClicked) {
x = me.getX() - dX;
y = me.getY() - dY;
cX = x + r;
cY = y + r;
repaint();
}
}
public void mouseReleased(MouseEvent e) {
isCircleClicked = false;
}
}
}
Your one problem is with
public void mouseDragged(MouseEvent me) {
if (isCircleClicked = true) {
What you're doing here is setting isCircleClicked to true every time you drag the mouse. Also this statement evaluates to true which is why you can drag anywhere and move the circle. Change this to
if (isCircleClicked) {
and you should be fine here.
The next problem is that you never reset isCircleClicked to false. You should be doing this either in mouseReleased or change your mousePressed as follows:
public void mousePressed(MouseEvent me) {
isCircleClicked =
(cX - me.getX()) * (cX - me.getX()) +
(cY - me.getY)) * (cY - me.getY()) < r * r;
}
which will set isCircleClicked accordingly.
There is still something to do, though. In the current form you need to start dragging to the upper-left of the center point, as illustrated below:
+------+
| |
| .-|-.
| / | \
+------+ |
\ /
'-_-'
This is because of your drawing: fillOval takes an upper-left corner of the oval and a width and height of the bounding rectangle. It's not the center point and the respective diameters. Hence you need to adapt his as follows:
g.fillOval(cX - r, cY - r, r * 2, r * 2);
Note the offset by the radius to the left and top.
Furthermore, your dragging code needs a bit more work. You are currently assuming that the user drags the circle's center. What you need to do is save the coordinates of the mouse click and then move the circle based on the mouse's movement relative to the last point. Currently you're moving relative to the circle's center so for the movement to be nice you have to start dragging exactly in the center of the circle. I'll leave that as an exercise for you :-)
Besides, your listener class already inherits from MouseAdapter so you don't need to explicitly implement the MouseMotionListener since MouseAdapter implements it already.
The structure of you program is wrong. You should never override the paint(...) method of a JFrame. That is an old AWT trick and should NOT be used with Swing.
Read the section from the Swing tutorial on Custom Painting for an example of the proper way to do painting. The basics are to override the paintComponent(...) method of a JPanel and then add the panel to the content pane of the frame.
With regards to you question a better solution is to create an Elllipse2D object to represent your circle. Then the custom painting can use the drawShape(...) method of the Graphics2D class. Then in you MouseListener code you can use the Shape.contains(...) method to see if the mouse was clicked on the circle.