JavaFx - Circle path animation and ScrollEvent - java

For a Circle Path Animation I copied the Code from this link
JavaFX 2 circle path for animation,
But I need this Code to do the following:
I would like to move the green rectangle with my MouseWheel, I'm not sure if and how this works with a ScrollEvent.
With a Button "Add Rectangle" I would like to add some rectangles to this Circle path
Any help would be greatly appreciated

I actually forgot to put an answer to this, so here it is:
public void start(Stage primaryStage) throws Exception {
// Choose a Object to move around the Path
Circle objectCircle = new Circle();
objectCircle.setRadius(10);
// Use a Shape for the Path or...
Circle pathCircle = new Circle(300, 200, 150);
pathCircle.setFill(null);
pathCircle.setStroke(Color.BLACK);
// ...use different approach (see createEllipsePath below) or...
Path path = createEllipsePath(400, 200, 150, 150, 0);
// ...use Class MoveTo & CubicCurveTo
//path.getElements().add (new MoveTo (0f, 50f));
//path.getElements().add(new CubicCurveTo(40f, 10f, 390f, 240f, 1904, 50f));
// Set up a Path Transition
PathTransition pathTransitionCircle = new PathTransition();
pathTransitionCircle.setDuration(Duration.seconds(10));
pathTransitionCircle.setNode(objectCircle);
pathTransitionCircle.setPath(pathCircle);
pathTransitionCircle.setCycleCount(Timeline.INDEFINITE);
pathTransitionCircle.setAutoReverse(false);
pathTransitionCircle.setInterpolator(Interpolator.LINEAR);
pathTransitionCircle.play();
BorderPane root = new BorderPane();
root.getChildren().addAll(pathCircle,objectCircle);
// Pause Transition
root.setOnMousePressed(eve -> pathTransitionCircle.pause());
// Set up the ScrollEvent
root.setOnScroll(event -> {
//SCROLL UP
double currentTime = pathTransitionCircle.getCurrentTime().toMillis();
double jumpRate = 100; // change rate for smaller/bigger jumps
pathTransitionCircle.jumpTo(Duration.millis(currentTime + jumpRate));
//SCROLL DOWN
double deltaY = event.getDeltaY();
if (deltaY < 0) {
if (currentTime == 0) {
currentTime = 10000; // end of circle is 10000millis: at second 0 we cannot jump to negative seconds, so we set it to 10000millis
pathTransitionCircle.jumpTo(Duration.millis(currentTime - jumpRate));
} else {
pathTransitionCircle.jumpTo(Duration.millis(currentTime - jumpRate));
}
}
});
primaryStage.setTitle("Path Transition ScrollEvent");
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
//create an EllipsePath with ArcTo
private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate) {
ArcTo arcTo = new ArcTo();
arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree circle
arcTo.setY(centerY - radiusY);
arcTo.setSweepFlag(false);
arcTo.setLargeArcFlag(true);
arcTo.setRadiusX(radiusX);
arcTo.setRadiusY(radiusY);
arcTo.setXAxisRotation(rotate);
Path pathCircle = new Path();
pathCircle.getElements().addAll(new MoveTo(centerX - radiusX, centerY - radiusY),arcTo);
pathCircle.getElements().add(new ClosePath());
// Styling
pathCircle.setStroke(Color.RED);
pathCircle.getStrokeDashArray().setAll(5d, 5d);
return pathCircle;
}
public static void main(String[] args) { launch(args); }
}

Related

How to move an existing Line shape in java using a mouse?

I would like to drag a selected line, but I'm unsure how to do it.
I tried the following but it doesn't work and the line doesn't move correctly. What calculations am I missing?
var deltaX = mouseEvent.x - (line.startX + line.endX)
var deltaY = mouseEvent.y - (line.startY + line.endY)
line.startX = line.startX + deltaX
line.startY = line.startY +deltaY
line.endX = line.endX + deltaX
line.endY = line.endY +deltaY
moving line with mouse
moving a line using mouse event
App.java
public class App extends Application {
private double startX;
private double startY;
#Override
public void start(Stage stage) throws Exception {
Line line = new Line(0, 0, 0, 200);
line.setStrokeWidth(8);
Shape endHandler = new Circle(15, Color.BLUE);
endHandler.setStroke(Color.BLACK);
line.setOnMousePressed(e -> {
startX = line.getLayoutX() - e.getX();
startY = line.getLayoutY() - e.getY();
});
line.setOnMouseDragged(e -> {
line.setTranslateX(line.getTranslateX() + e.getX()+startX);
line.setTranslateY(line.getTranslateY() + e.getY()+startY);
});
Group group = new Group(line);
group.setTranslateX(250);
group.setTranslateY(250);
Scene scene = new Scene(new AnchorPane(group), 500, 500);
stage.setTitle("moving line");
stage.setScene(scene);
stage.show();
}
}
moving by binding start x y and end x y
In this aproach we are dragging circle shapes and the start and the end of line object will follow those shapes 'cause they are binded
This is a functional single javafx app you can try .
red circle is for start line position and blue is for end
public class App extends Application {
#Override
public void start(Stage stage) throws Exception {
Line line = new Line(0, 0, 0, 200);
Shape endHandler = new Circle(15, Color.BLUE);
endHandler.setStroke(Color.BLACK);
Shape startHandler = new Circle(15, Color.RED);
startHandler.setStroke(Color.BLACK);
endHandler.translateXProperty().bindBidirectional(line.endXProperty());
endHandler.translateYProperty().bindBidirectional(line.endYProperty());
startHandler.translateXProperty().bindBidirectional(line.startXProperty());
startHandler.translateYProperty().bindBidirectional(line.startYProperty());
endHandler.setOnMouseDragged(e -> {
endHandler.setTranslateX(endHandler.getTranslateX() + e.getX());
endHandler.setTranslateY(endHandler.getTranslateY() + e.getY());
});
startHandler.setOnMouseDragged(e -> {
startHandler.setTranslateX(startHandler.getTranslateX() + e.getX());
startHandler.setTranslateY(startHandler.getTranslateY() + e.getY());
});
Group group = new Group(line, endHandler, startHandler);
group.setTranslateX(250);
group.setTranslateY(250);
Scene scene = new Scene(new AnchorPane(group), 500, 500);
stage.setTitle("moving line");
stage.setScene(scene);
stage.show();
}
}
This code cancels out the start / end position.
for
line.startX = line.startX + deltaX
=
line.startX = line.startX +(mouseEvent.x - (line.startX + line.endX))
=
line.startX = -line.endX+mouseEvent.x
So now you dont even have the start position
You should have
var deltaX=mouseEvent.x
var deltaY = mouseEvent.y
line.startX = line.startX + deltaX
line.startY = line.startY +deltaY
line.endX = line.endX + deltaX
line.endY = line.endX +deltaY
Make sure the mouse Positions update

Rotate a group of rectangles around their common center

I have a few rectangles that I assign rotation one by one ((javafx.scene.shape.Rectangle) shape).rotateProperty().bind(rotate);
For example, 45 degrees
My rectangle rotates around its center. Please tell me how to make the enemy a few right-angles around their common center.
To get something like this
this.rotate.addListener((obs, old, fresh) -> {
for (VObject vObject : children ) {
vObject.rotate.set(this.rotate.get());
}
});
This is how I add rotation. How can I specify the angle
Update: I used the advice below and now I set the rotation individually for each rectangle. (The selection is still a bit wrong)
this.rotate.addListener((obs, old, fresh) -> {
Rotate groupRotate = new Rotate(rotate.get(),
this.x.getValue().doubleValue() + this.width.getValue().doubleValue() / 2 ,
this.y.getValue().doubleValue() + this.height.getValue().doubleValue() / 2);
for (VObject vObject : children ) {
vObject.getShape().getTransforms().clear();
vObject.getShape().getTransforms().add(groupRotate);
}
});
But now the axis also rotates depending on the rotation.
Can I set the rotation to the rectangles without turning the coordinate axis?
If you put all rectangles into a common Group, you can rotate them at once:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class RotateAllApplication extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
// the common group
Group group = new Group();
group.getChildren().addAll(new Rectangle(10, 10, 80, 40), //
new Rectangle(110, 10, 80, 40), //
new Rectangle(10, 110, 80, 40), //
new Rectangle(110, 110, 80, 40));
// rotate the group instead of each rectangle
group.setRotate(45.0);
root.setCenter(group);
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Update: If you don't want to create a parent Group object, you can apply the same rotation transformation to each child instead. While Node#setRotate(double) always rotates around the center of the node, adding a transformation to Node#getTransforms() is more general and not restricted to simple rotations.
The following statement will apply a rotation around the point (100.0/100.0) of the parent coordinate system to all children in the list:
childrenList.forEach(child -> child.getTransforms().add(Transform.rotate(45.0, 100.0, 100.0)));
Use a Rotate transform and specify the appropriate pivot point:
#Override
public void start(Stage primaryStage) throws IOException {
Pane pane = new Pane();
pane.setPrefSize(600, 600);
Rectangle[] rects = new Rectangle[4];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
Rectangle rect = new Rectangle(100 + i * 200, 100 + j * 100, 150, 50);
rects[i * 2 + j] = rect;
pane.getChildren().add(rect);
}
}
Slider slider = new Slider(0, 360, 0);
double minX = Double.POSITIVE_INFINITY;
double minY = Double.POSITIVE_INFINITY;
double maxX = Double.NEGATIVE_INFINITY;
double maxY = Double.NEGATIVE_INFINITY;
Rotate rotate = new Rotate();
// find pivot point
for (Rectangle rect : rects) {
double val = rect.getX();
if (minX > val) {
minX = val;
}
val += rect.getWidth();
if (maxX < val) {
maxX = val;
}
val = rect.getY();
if (minY > val) {
minY = val;
}
val += rect.getHeight();
if (maxY < val) {
maxY = val;
}
rect.getTransforms().add(rotate);
}
rotate.setPivotX(0.5 * (maxX + minX));
rotate.setPivotY(0.5 * (maxY + minY));
rotate.angleProperty().bind(slider.valueProperty());
Scene scene = new Scene(new VBox(10, pane, slider));
primaryStage.setScene(scene);
primaryStage.sizeToScene();
primaryStage.show();
}
If you're planing to apply multiple transformations, you may need to adjust the code for finding the pivot point to use transforms for calculating the bounds...

Switch forms color when it is inside another one

I am trying to set a panel that contains several points and then draw a triangle or any others forms. And all points that are inside that form will switch their colors to red.
Seems like i am doing something wrong, is this the correct way to draw point inside the for loop ?
Make sure the Polygon is created before creating the circles. This allows you to use check for intersection of the shapes as described in this answer: https://stackoverflow.com/a/15014709/2991525
Choose the circle fill accordingly:
#Override
public void start(Stage stage) {
Group root = new Group();
Polygon triangle = new Polygon(300d, 100d, 600d, 150d, 500d, 300d);
root.getChildren().add(triangle);
Scene scene = new Scene(root, 900, 500);
for (double x = 65; x < scene.getWidth(); x += 65) {
for (double y = 65; y < scene.getHeight(); y += 65) {
Circle circle = new Circle(x, y, 10);
root.getChildren().add(circle);
Shape intersection = Shape.intersect(circle, triangle);
//Setting the color of the circle
circle.setFill(intersection.getBoundsInLocal().getWidth() == -1 ? Color.BLACK : Color.RED);
}
}
triangle.setFill(Color.TRANSPARENT);
triangle.setStroke(Color.RED);
triangle.toFront();
stage.setTitle("Scale transition example");
stage.setScene(scene);
stage.show();
}

JavaFX - Interaction between circle and rectangle

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.

Find an angle representing the direction of travel from origin coordinates

I'm working on an animation where a number of triangular objects move across the screen. In order to ensure each faces the direction in which it is travelling, I need to rotate the image by the appropriate angle.
My problem is that the angle my code returns does not accurately do this. The values returned do not change by more than a few degrees.
/**
* Accepts two grid positions are arguments. The current position
* of the object and the next grid position. Returns an angle representing
* the direction of travel from the current position towards the next position. By converting the Cartesian coordinates into polar coordinates.
*
*/
public void setAngle(Vector2d currentPos, Vector2d nextPos ) {
Double delta_x = current.xPos - next.xPos;
Double delta_y = current.yPos - next.yPos;
Double theta = Math.atan2(delta_y, delta_x);
this.angle = theta;
}
Example:
|| current: 1031.1438073417544 , 268.3133503758045 || next: 1033.101761841174 , 269.0819944286846 || Angle: 0.0
|| current: 1033.1901579769194 , 242.19363555578593 || next: 1035.1281222295695 , 243.08778242413436 || Angle: 0.0
|| current: 1022.1577455080815 , 255.24422527831163 || next: 1024.0301966330894 , 256.19078788718997 || Angle: 0.0
// calc the deltas as next minus current
double delta_x = next.xPos - current.xPos;
double delta_y = next.yPos - current.yPos;
// Calc the angle IN RADIANS using the atan2
double theta = Math.atan2(delta_y, delta_x);
// this.angle is now in degrees
// or leave off *180/Math.PI if you want radians
this.angle = theta*180/Math.PI;
Here's how you create a complete example.
A Path (ellipse path by Uluk) on which a Node (triangle) is moved. During each movement the rotation of the node is calculated and adjusted.
public class Main extends Application {
double prevX = 0;
double prevY = 0;
double radiusX = 200;
double radiusY = 100;
double centerX = 600;
double centerY = 200;
#Override
public void start(Stage primaryStage) {
try {
// race track
Path path = new Path();
MoveTo moveTo = new MoveTo(centerX - radiusX, centerY - radiusY);
ArcTo arcTo = new ArcTo();
arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree celsius circle.
arcTo.setY(centerY - radiusY);
arcTo.setSweepFlag(false);
arcTo.setLargeArcFlag(true);
arcTo.setRadiusX(radiusX);
arcTo.setRadiusY(radiusY);
path.getElements().add(moveTo);
path.getElements().add(arcTo);
path.getElements().add(new ClosePath());
path.setFill(Color.TRANSPARENT);
path.setStroke(Color.LIGHTGRAY);
path.setStrokeWidth(40);
Pane root = new Pane();
// car: arrow pointing right
Polygon arrow = new Polygon(0, 0, 20, 10, 0, 20);
root.getChildren().addAll( path, arrow);
// lawn
Scene scene = new Scene(root, 800, 400, Color.GREEN);
primaryStage.setScene(scene);
primaryStage.show();
// movement: car path on race track
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(4000));
pathTransition.setPath(path);
pathTransition.setNode(arrow);
pathTransition.setCycleCount(Timeline.INDEFINITE);
pathTransition.setAutoReverse(true);
// rotate car during movement
pathTransition.currentTimeProperty().addListener(e -> {
double rad = angle(new Point2D(prevX, prevY), new Point2D(arrow.getTranslateX(), arrow.getTranslateY()));
double deg = Math.toDegrees(rad);
arrow.setRotate(deg);
prevX = arrow.getTranslateX();
prevY = arrow.getTranslateY();
});
pathTransition.play();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
/**
* Calculate angle between source and target point in radians.
* #param source
* #param target
* #return
*/
public static double angle(Point2D source, Point2D target) {
double dx = target.getX() - source.getX();
double dy = target.getY() - source.getY();
double angle = Math.atan2(dy, dx);
return angle;
}
}

Categories