I have a program where 2 circles are dragged in a pane. There is also a line connecting them and the distance displayed above it. My problem lies when I drag the circles at a slow pace with the mouse they move fine, but when I move it more quickly the circles stop.
here is where the circle drag is calculated
pane.setOnMouseDragged(e -> {
if (circle1.contains(e.getX(), e.getY())) {
pane.getChildren().clear();
circle1.setCenterX(e.getX());
circle1.setCenterY(e.getY());
pane.getChildren().addAll(getLine(circle1, circle2), circle1,
circle2, getText(circle1, circle2));
}
else if (circle2.contains(e.getX(), e.getY())) {
pane.getChildren().clear();
circle2.setCenterX(e.getX());
circle2.setCenterY(e.getY());
pane.getChildren().addAll(getLine(circle1, circle2), circle1,
circle2, getText(circle1, circle2));
}
});
I think what is happening is that when the mouse moves quickly, the distance moved between processing two consecutive events takes it outside the bounds of the circle, so the if condition becomes false. You probably need to register the mouse handlers on the circles themselves, instead of the pane. (As an aside, why clear and rebuild the pane, instead of just update the line?)
Here's an example using these techniques:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class DraggingCircles extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Circle circle1 = createDraggingCircle(50, 50, 25, Color.BLUE);
Circle circle2 = createDraggingCircle(350, 350, 25, Color.RED);
Line line = new Line();
line.startXProperty().bind(circle1.centerXProperty());
line.startYProperty().bind(circle1.centerYProperty());
line.endXProperty().bind(circle2.centerXProperty());
line.endYProperty().bind(circle2.centerYProperty());
pane.getChildren().addAll(circle1, circle2, line);
Scene scene = new Scene(pane, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private Circle createDraggingCircle(double x, double y, double radius, Color fill) {
Circle circle = new Circle(x, y, radius, fill);
ObjectProperty<Point2D> mouseLocation = new SimpleObjectProperty<>();
circle.setOnMousePressed(e -> {
mouseLocation.set(new Point2D(e.getX(), e.getY()));
});
circle.setOnMouseDragged(e -> {
double deltaX = e.getX() - mouseLocation.get().getX();
double deltaY = e.getY() - mouseLocation.get().getY();
circle.setCenterX(circle.getCenterX() + deltaX);
circle.setCenterY(circle.getCenterY() + deltaY);
mouseLocation.set(new Point2D(e.getX(), e.getY()));
});
return circle ;
}
public static void main(String[] args) {
launch(args);
}
}
Related
In my application, I'm drawing a lot of circles of different radii on a javafx canvas using the fillOval and strokeOval methods. I am trying to optimize this draw operation by drawing only those circles which will be visible(partially also) in my screen. So I want to know is there a method that can tell me that drawing this circle, text, line, image etc. will be visible in the screen.
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
Pane pane;
Canvas canvas;
double transX = 0.0, transY = 0.0;
public void draw() {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.clearRect(canvas.getLayoutX(), canvas.getLayoutY(), canvas.getWidth(), canvas.getHeight());
gc.setStroke(Color.FIREBRICK);
gc.setFill(Color.TRANSPARENT);
gc.setLineWidth(2.0);
for(int i = 1; i <= 10000; ++i) {
double r = 10 * i;
// some function to check the below drawing operations will be visible or not
// Pls. note centre of the circles need not be at the centre of the screen, if canvas is panned to right/left/top/bottom by mouse drag operation where translation values will be stored in transX and transY
gc.fillOval(pane.getWidth() / 2 - r + transX, pane.getHeight() / 2 - r + transY, 2 * r, 2 * r);
gc.strokeOval(pane.getWidth() / 2 - r + transX, pane.getHeight() / 2 - r + transY, 2 * r, 2 * r);
}
}
#Override
public void start(Stage primaryStage) {
pane = new Pane();
canvas = new Canvas();
canvas.layoutXProperty().bind(pane.layoutXProperty());
canvas.layoutYProperty().bind(pane.layoutYProperty());
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
pane.widthProperty().addListener((observableValue, number, t1) -> draw());
pane.heightProperty().addListener((observableValue, number, t1) -> draw());
pane.getChildren().addAll(canvas);
primaryStage.setScene(new Scene(pane, primaryStage.getWidth(), primaryStage.getHeight()));
primaryStage.setTitle("Canvas Demo");
primaryStage.setMaximized(true);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
I'm trying to create a program, which should draw a rectangle on a canvas based on two left mouse clicks and clears the canvas with one right click.
The rectangle should be created in a way, where the first mouse click simulates one corner of the rectangle, and the next mouse click simulates the diagonal corner of the rectangle compared to the first mouse click.
I'm stuck on how to store the coordinates for the first mouse click, and then put the second mouse click to good use, as the rectangle per definition is created based on only 1 set of coordinates, which is the upper-left corner of the rectangle.
Right now, all my code does is that it draws fixed sized rectangles (50x25) which obviously isn't what I want. This is just to see if the clearing part works.
This is what I've got so far:
package application;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class DrawRectangle extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Title");
Canvas canv = new Canvas(500, 400);
GraphicsContext gc = canv.getGraphicsContext2D();
EventHandler<MouseEvent> event = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY) {
double x = event.getX();
double y = event.getY();
gc.setFill(Color.BLUE);
gc.fillRect(x, y, 50, 25);
}
}
};
canv.addEventHandler(MouseEvent.MOUSE_CLICKED, event);
EventHandler<MouseEvent> event2 = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event2) {
if (event2.getButton() == MouseButton.SECONDARY) {
gc.clearRect(0, 0, 500, 400);
}
}
};
canv.addEventHandler(MouseEvent.MOUSE_CLICKED, event2);
StackPane root = new StackPane();
root.getChildren().add(canv);
primaryStage.setScene(new Scene(root, 500, 400));
primaryStage.show();
}
}
I don't know if it matters which corners of the rectangle the mouse clicks should simulate, maybe someone got any ideas?
Best regards and happy new year
Just save the first click point in a member variable, then use it and reset it when the user makes a second click point.
Note: the sample code below uses a switch expression from Java 13+. If switch expressions are not available in your Java version, convert it to a standard switch statement or if/else clauses.
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.canvas.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class DrawRectangle extends Application {
private static final int W = 500;
private static final int H = 400;
private Point2D savedPoint = null;
#Override
public void start(Stage stage) {
Canvas canvas = new Canvas(W, H);
GraphicsContext gc = canvas.getGraphicsContext2D();
canvas.setOnMouseClicked(event -> {
switch (event.getButton()) {
case PRIMARY -> handlePrimaryClick(gc, event);
case SECONDARY -> gc.clearRect(0, 0, W, H);
}
});
stage.setResizable(false);
stage.setScene(new Scene(new StackPane(canvas), W, H));
stage.show();
}
private void handlePrimaryClick(GraphicsContext gc, MouseEvent event) {
Point2D clickPoint = new Point2D(event.getX(), event.getY());
if (savedPoint == null) {
savedPoint = clickPoint;
} else {
Rectangle2D rectangle2D = getRect(savedPoint, clickPoint);
gc.setFill(Color.BLUE);
gc.fillRect(
rectangle2D.getMinX(),
rectangle2D.getMinY(),
rectangle2D.getWidth(),
rectangle2D.getHeight()
);
savedPoint = null;
}
}
private Rectangle2D getRect(Point2D p1, Point2D p2) {
return new Rectangle2D(
Math.min(p1.getX(), p2.getX()),
Math.min(p1.getY(), p2.getY()),
Math.abs(p1.getX() - p2.getX()),
Math.abs(p1.getY() - p2.getY())
);
}
public static void main(String[] args) {
launch(args);
}
}
I just started working with JavaFX and have a question. In my project i want to use a rotating rectangle. But the rectangle only rotates about its center and i want it to rotate around its upper left corner.
Like in this picture (from here):
Here some code like in my project:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.scene.shape.Rectangle;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.*;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
Group root = new Group();
Scene scene = new Scene(root, 500, 500);
//create rectangle
Rectangle rect = new Rectangle(10, 10, 200, 15);
rect.setTranslateX(250);
rect.setTranslateY(250);
rect.setFill(Color.BLACK);
root.getChildren().add(rect);
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
stage.getScene().setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.LEFT) {
rect.setRotate(rect.getRotate()-5); //<-- rotate rectangle here
} else if (e.getCode() == KeyCode.RIGHT){
rect.setRotate(rect.getRotate()+5); //<-- rotate rectangle here
}
});
}
};
timer.start();
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In this case the rectangle rotates if the arrow keys are pressed.
You need to use Transform and Rotate specifically to rotate a node around a custom pivot.
//Create a Rotate Object
Rotate rotate = new Rotate();
rotate.setPivotX(node.getX()); //Pivot X Top-Left corner
rotate.setPivotY(node.getY()); //Pivot Y
rotate.setAngle(angle); //Angle degrees
//Add the transform to the node
node.getTransforms().add(rotate);
In your code
final Rotate rotate = new Rotate();
rect.getTransforms().add(rotate);
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
stage.getScene().setOnKeyPressed(e -> {
rotate.setPivotX(rect.getX());
rotate.setPivotY(rect.getY());
if (e.getCode() == KeyCode.LEFT) {
rotate.setAngle(rotate.getAngle() - 5);
} else if (e.getCode() == KeyCode.RIGHT){
rotate.setAngle(rotate.getAngle() + 5);
}
});
}
};
I am working on a project that has to do with animation in javaFx, I wrote the GUI for it and it is a Stick Figure. I am trying to make the stick figure walk to the right side of the pane and then when it touches the right side turn and go back the other way all the way to the left wall of the pane. I have this code that is a moving ball that does exactly what I want for my stick figure to do but I cannot seem to modify that code into my stick figure program. any ideas on how to do this? both codes are below:
package animationdemo;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class MovingBallDemo_3 extends Application {
#Override
public void start(Stage primaryStage) {
BallPane ballPane = new BallPane(); // Create a ball pane
// Pause and resume animation
ballPane.setOnMousePressed(e -> ballPane.pause());
ballPane.setOnMouseReleased(e -> ballPane.play());
// Create a scene and place it in the stage
Scene scene = new Scene(ballPane, 250, 150);
primaryStage.setTitle("Bouncing Ball Control"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
public static void main(String[] args) {
launch(args);
}
}
class BallPane extends Pane {
public final double radius = 20;
private double x = 2 * radius, y = 3 * radius;
private double dx = 3; // Number of pixels to move each time
private Circle circle = new Circle(x, y, radius);
private Timeline animation;
public BallPane() {
circle.setFill(Color.RED); // Set ball color
getChildren().add(circle); // Place a ball into this pane
// Create the animation for 25 millisecond events
animation = new Timeline(new KeyFrame(Duration.millis(25), e -> moveBall()));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
}
public void play() {
animation.play();
}
public void pause() {
animation.pause();
}
// Move the ball. When a wall is encountered, reverse direction
protected void moveBall() {
if (x <= radius || x >= getWidth() - radius) {
dx *= -1; // Change direction
}
// Adjust ball position
x += dx;
circle.setCenterX(x);
}
}
the above is the animation that I want the stick figure GUI to do. here is my attempt of merging my code in with the code above to make the stick figure work but nothing happens:
package Stickfigure;
import java.awt.Graphics;
import javafx.animation.KeyFrame;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Stickfigure extends Application {
#Override
public void start(Stage primaryStage) {
BallPane ballPane = new BallPane();
ballPane.setOnMousePressed(e -> ballPane.pause());
ballPane.setOnMouseReleased(e -> ballPane.play());
Circle circle = new Circle(100, 100, 0);//head
Circle circle1 = new Circle(120, 80, 50);//eye
circle1.setRadius(5);//radius of eye
circle.setRadius(50);//radius of head
circle.setStroke(Color.BLACK);//circle color
circle1.setStroke(Color.BLACK);//circle color
circle.setFill(null);//makes the head empty(no brain haha)
circle.setStrokeWidth(5);//sets the line thickness of circle (head)
Arc arc = new Arc();//mouth
arc.setCenterX(110.0f);//mouth position
arc.setCenterY(120.0f);//mouth position
arc.setRadiusX(35.0f);//mouth size
arc.setRadiusY(25.0f);//mouth size
arc.setStartAngle(1.0f);//angle of mouth
arc.setLength(5.0f);//length of mouth
arc.setType(ArcType.ROUND);
Line line1 = new Line(100, 250, 100, 150); //body of stick figure
Line line2 = new Line(); //left leg
Line line3 = new Line();//right leg
Line line4 = new Line();//right arm
Line line5 = new Line();//left arm
line2.setStartX(30.0f); //left leg starting position y
line2.setStartY(350.0f);//left leg starting position y
line2.setEndX(100.0f);//left leg end pos x
line2.setEndY(250.0f);//left leg end pos y
line3.setStartX(200.0f); //right leg start pos x
line3.setStartY(350.0f);// right leg start pos y
line3.setEndX(100.0f); //right leg end pos x
line3.setEndY(250.0f); //right leg end pos y
line4.setStartX(100.0f);//right arm start pos x
line4.setStartY(200.0f); //right arm start pos y
line4.setEndX(200.0f); //right arm end pos x
line4.setEndY(170.0f); //right arm end pos y
line5.setStartX(30.0f);//left arm arm statt pos x
line5.setStartY(250.0f); // left arm start pos y
line5.setEndX(100.0f);//left arm end pos x
line5.setEndY(200.0f);//left arm end pos y
line1.setStrokeWidth(5); //thickness of line
line1.setStroke(Color.BLACK);//color of line
line2.setStrokeWidth(5);//thickness of line
line2.setStroke(Color.BLACK);//color of line
line3.setStrokeWidth(5);//thickness of line
line3.setStroke(Color.BLACK);//color of line
line4.setStrokeWidth(5);//thickness of line
line4.setStroke(Color.BLACK);//color of line
line5.setStrokeWidth(5);//thickness of line
line5.setStroke(Color.BLACK);//color of line
// Create a pane to hold the circle
ballPane.getChildren().add(circle); //adds circle to picture
ballPane.getChildren().add(circle1);//adds circle to picture
ballPane.getChildren().add(line1);//adds line
ballPane.getChildren().add(line2);//adds line
ballPane.getChildren().add(line3);//adds line
ballPane.getChildren().add(line4);//adds line
ballPane.getChildren().add(line5);//adds line
ballPane.getChildren().add(arc);//adds arc
Scene scene = new Scene( ballPane, 400, 400);
primaryStage.setTitle("stick figure");//title
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
class BallPane extends Pane {
public final double radius = 20;
private double x = 2 * radius, y = 3 * radius;
private double dx = 3; // Number of pixels to move each time
private Timeline animation;
public BallPane() {
// Create the animation for 25 millisecond events
animation = new Timeline(new KeyFrame(Duration.millis(25), e -> moveBall()));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
}
public void play() {
animation.play();
}
public void pause() {
animation.pause();
}
// Move the ball. When a wall is encountered, reverse direction
protected void moveBall() {
if (x <= radius || x >= getWidth() - radius) {
dx *= -1; // Change direction
}
// Adjust ball position
x += dx;
}
}
}
You are not doing any moving in the moveBall() method. You just change local values. Have a look at the Node class from which Pane is inheriting and the translateXProperty. You want to change that, not just your fields.
You even had the actual translation in your Ball class: circle.setCenterX(x);
protected void moveBall() {
if (x <= radius || x >= getWidth() - radius) {
dx *= -1; // Change direction
}
// Adjust ball position
x += dx;
//#######################################
setTranslateX(x); // Move the Pane!!! ###
//#######################################
}
I am making a javafx application that creates a bounded rectangle around the circles the user creates when primary mouse clicking. The user can also remove a circle with the secondary mouse button and the bounding rectangle should react accordingly and use the remaining circles as its upper and lower limits. The problem with my program is that it does not work when I try to remove more than one circle in a row (it only removes and resizes for the last circle)
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Collections;
public class BoundRectangle extends Application {
double minX, maxX, minY, maxY;
Pane pane = new Pane();
Rectangle rectangle;
ArrayList<Circle> allCircles = new ArrayList<>();
#Override
public void start(Stage primaryStage) {
VBox mainBox = new VBox();
Pane mainPane = new Pane(mainBox);
mainPane.setPadding(new Insets(10, 10, 10, 10));
pane.getChildren().addAll(mainPane);
mainBox.setLayoutX(10);
mainBox.setLayoutY(10);
pane.setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.PRIMARY) {
Circle circle = new Circle(e.getX(), e.getY(), 10);
allCircles.add(circle);
pane.getChildren().add(drawRectangle());
pane.getChildren().add(circle);
System.out.println("maxX " + maxX);
System.out.println("maxY " + maxY);
System.out.println("minX " + minX);
System.out.println("minY " + minY);
circle.setOnMouseClicked(evt -> {
if (evt.getButton() == MouseButton.SECONDARY) {
pane.getChildren().remove(circle);
allCircles.remove(circle);
pane.getChildren().add(drawRectangle());
}
});
circle.setStroke(Color.BURLYWOOD);
circle.setStrokeWidth(3);
circle.setFill(Color.TRANSPARENT);
}
});
Scene scene = new Scene(pane, 600, 600);
primaryStage.setScene(scene);
primaryStage.setTitle("click circles, make rectangle");
primaryStage.show();
}
public Rectangle drawRectangle() {
refresh();
getMinMax();
if (pane.getChildren().size() == 1)
{
Rectangle rect0 = new Rectangle(0, 0, 0, 0);
return rect0;
}
Rectangle boundingRect = new Rectangle();
boundingRect.setX(minX - 10 - 2);
boundingRect.setY(minY - 10 - 2);
boundingRect.setWidth(maxX - minX + 2.0 * 10 + 2);
boundingRect.setHeight(maxY - minY + 2.0 * 10 + 2);
boundingRect.setStroke(Color.BLACK);
boundingRect.setStrokeWidth(2);
boundingRect.setFill(Color.TRANSPARENT);
return boundingRect;
}
public void getMinMax() {
maxY = allCircles.get(0).getCenterY();
minY = allCircles.get(0).getCenterY();
maxX = allCircles.get(0).getCenterX();
minX = allCircles.get(0).getCenterX();
for (Circle c : allCircles) {
if (c.getCenterX() < minX)
minX = c.getCenterX();
if (c.getCenterX() > maxX)
maxX = c.getCenterX();
if (c.getCenterY() < minY)
minY = c.getCenterY();
if (c.getCenterY() > maxY)
maxY = c.getCenterY();
}
}
private void refresh() {
ObservableList<Node> list = pane.getChildren();
for (Node c : list) {
if (c instanceof Rectangle) {
pane.getChildren().remove(c);
break;
}
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
The last circle you add appears on top of the rectangle; however the rectangle appears on top of all the other circles. So only the last circle added will get the mouse clicks (clicks on other circles are targeted to the rectangle).
The quickest fix is to make the rectangle mouse transparent:
boundingRect.setMouseTransparent(true);
A better solution overall might be just to create one rectangle and update its x, y, width and height properties. That way you can just ensure the rectangle is added once and remains at the bottom of the stack.