JavaFX - Interaction between circle and rectangle - java

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.

Related

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 - Circle path animation and ScrollEvent

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); }
}

javafx, dragging optimisation - various ideas

I have an app with lots of draggable Nodes, which can get a bit slow. In particular when:
dragging a Node containing other nodes or
dragging Node inside another IF it results in widening of the parent's Bounds [almost as if it was caching some range-related data, common for double values inside, and needed to do extra processing if range changed] (EDIT: this has now been explained to be the parent's layout pass)
Q: Is there anything to gain by doing some of the operations (the few for which I can control types) on integers (converting to ints first) and then re-assigning to doubles?
(EDIT: this has now been answered)
Q1: How else could I speed it up?
==========UPDATE:
Thanks to advice so far I determined my main problem is panning a Region container inside a Region root, as it triggers a layout pass in the container Region, which in turn (I think) ping-pongs to do a layout pass in all the (ca. 40) children Nodes (which are complex in their own right - they contain textfields, buttons, etc.).
So far the fastest solution I found is panning a Group in a Group (already a lot faster), where the Groups are additionally modified (subclass, override compute... methods) to make sure the parent/root will not need to be resized during dragging (sadly, not so noticeable).
class FastGroup extends Group {
//'stop asking about size' functionality
double widthCache;
double heightCache;
protected double computePrefWidth(double height) {
return widthCache != 0 ? widthCache : super.computePrefWidth(height);
}
protected double computePrefHeight(double width) {
return heightCache != 0 ? heightCache : super.computePrefHeight(width);
}
public void initDragging(){
//if a child of this Group goes into drag
//add max margins to Group's size
double newW = getBoundsInLocal().getWidth() + TestFXApp03.scene.getWidth()*2;
double newH = getBoundsInLocal().getHeight() + TestFXApp03.scene.getHeight()*2;
//cache size
widthCache = newW;
heightCache = newH;
}
public void endDragging(){
widthCache = 0;
heightCache = 0;
}
}
However Group creates problems of its own.
Q3: why can't I achieve the same with Pane (tried that as a 3rd option)? According to the Docs, a Pane:
'does not perform layout beyond resizing resizable children to their
preferred size'
...while a Group:
'will "auto-size" its managed resizable children to their preferred
sizes during the layout pass'
'is not directly resizable'
...which sounds quite the same to me, and yet the Pane used as root results in very slow panning of the contained Group.
Ok... I spent some time in checking the performance of mouse-drags with many objects....
I did it on a Mac Pro (4 core, 8GB RAM).
A mouse drag using setTranslateX() and setTranslateY() in the setOnMouseDragged() handler is called at an interval of 17ms (60Hz).
If I add small circles to the drag-group, the drag keeps its pace up to 25.000 circle dragged along.
At 100.000 circles the drag interval is about 50ms (and shows some stops during drag)
At 1.000.000 circles the drag interval is about 500ms
If I implement the drag as image drag using the snapshot() function, the snapshot of 1.000.000 circles takes 600ms. Then the drag is smooth and fast.
Here the full code with and without using an image during drag:
public class DragMany extends Application {
Point2D lastXY = null;
Scene myScene;
long lastDrag;
ImageView dragImage;
void fill(Group box) {
Color colors[] = new Color[]{Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW, Color.PINK, Color.MAGENTA };
for (int i = 0; i < 100000; i++) {
Circle c = new Circle(2);
box.getChildren().add(c);
c.setFill(colors[(i/17) % colors.length]);
c.setLayoutX(i % 30 * 3);
c.setLayoutY((i/20) % 30*3);
}
box.setLayoutX(40);
box.setLayoutY(40);
}
void drag1(Pane pane, Group box) {
box.setOnMousePressed(event -> {
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
lastDrag = System.currentTimeMillis();
});
box.setOnMouseDragged(event -> {
event.setDragDetect(false);
Node on = box;
double dx = event.getSceneX() - lastXY.getX();
double dy = event.getSceneY() - lastXY.getY();
on.setLayoutX(on.getLayoutX()+dx);
on.setLayoutY(on.getLayoutY()+dy);
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
event.consume();
});
box.setOnMouseReleased(d -> lastXY = null);
}
void drag3(Pane pane, Group box) {
box.setOnMousePressed(event -> {
long now = System.currentTimeMillis();
lastXY = new Point2D(event.getSceneX(), event.getSceneY());
SnapshotParameters params = new SnapshotParameters();
params.setFill(Color.TRANSPARENT);
WritableImage image = box.snapshot(params, null);
dragImage = new ImageView(image);
dragImage.setLayoutX(box.getLayoutX());
dragImage.setLayoutY(box.getLayoutY());
dragImage.setTranslateX(box.getTranslateX());
dragImage.setTranslateY(box.getTranslateY());
pane.getChildren().add(dragImage);
dragImage.setOpacity(0.5);
box.setVisible(false);
System.out.println("Snap "+(System.currentTimeMillis()-now)+"ms");
pane.setOnMouseDragged(e -> {
if (dragImage == null) return;
Node on = dragImage;
double dx = e.getSceneX() - lastXY.getX();
double dy = e.getSceneY() - lastXY.getY();
on.setTranslateX(on.getTranslateX()+dx);
on.setTranslateY(on.getTranslateY()+dy);
lastXY = new Point2D(e.getSceneX(), e.getSceneY());
e.consume();
});
pane.setOnMouseReleased(e -> {
if (dragImage != null) {
box.setTranslateX(dragImage.getTranslateX());
box.setTranslateY(dragImage.getTranslateY());
pane.getChildren().remove(dragImage);
box.setVisible(true);
dragImage = null;
}
lastXY = null;
e.consume();
});
lastDrag = System.currentTimeMillis();
event.consume();
});
}
public void start(Stage primaryStage) {
Pane mainPane = new Pane();
myScene = new Scene(mainPane, 500, 500);
primaryStage.setScene(myScene);
primaryStage.show();
Group all = new Group();
fill(all);
mainPane.getChildren().add(all);
drag3(mainPane, all);
}
public static void main(String[] args) {
launch(args);
}
Using ints or floats instead of doubles won't give you much performance boost.
Instead, you should try optimizing when you change the position of nodes. For example, when you are dragging, instead of changing the position of all nodes, you could just render all the nodes that are being dragged at an offset and only change position of one node. Once you are done dragging, you could recalculate positions of all nodes you dragged.
Here is my guess of how you do it right now:
void drag(float x, float y) {
setPosition(x, y); // I'm gueessing this is where your bottle neck is
for (Node node : nodes) {
node.drag(x, y);
}
}
void render() {
screen.draw(this.x, this.y);
for (Node node : nodes) {
node.render();
}
}
Here is how I would optimize it by introducing render offset, so that you only set position of all the nodes when you are finished dragging:
float xo, yo; // render offsets
void drag(float x, float y) {
xo = x;
yo = y;
}
void dragEnd(float x, float y) {
setPosition(x, y);
for (Node node : nodes) {
node.dragEnd(x, y);
}
}
void render(float xo, float yo) {
xo += this.xo;
yo += this.yo;
screen.draw(this.x + xo, this.y + yo); // render with at offset
for (Node node : nodes) {
node.render(xo, yo);
}
}

Java Methods to rotate, increase size, and clear Box image in Java

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.

Manipulate Arc with mouse (JavaFX 2)

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

Categories