Im trying to draw circles inside of each other which have the same centres.
But the width should be different for each circle - it should be done inside a while loop.
The result should look like the picture i have uploaded:
My code is shown below:
package modelwhile;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class exercise4_figure3 extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
GridPane root = initContent();
Scene scene = new Scene(root);
stage.setTitle("Loops");
stage.setScene(scene);
stage.show();
}
private GridPane initContent() {
GridPane pane = new GridPane();
Canvas canvas = new Canvas(200, 200);
pane.add(canvas, 0, 0);
drawShapes(canvas.getGraphicsContext2D());
return pane;
}
// ------------------------------------------------------------------------
// circle figure begins here
private void drawShapes(GraphicsContext gc) {
int x = 80;
int y = 80;
int r1 = 20;
int r2 = 60;
while (r1 <= 80) {
gc.strokeOval(x - r2, y - r2, r1, r2);
r1 = r1 + 10;
}
}
}
any help would be appreciated.
The issue is that you aren't moving on the x-axis to account for the added width of each new oval. You need to move half the distance being added to the oval in order to keep them all in the same relative position.
Below is your drawShapes() method updated to include this movement. You'll notice I removed your x, y, and r2 variables because they didn't really have any need to be variables since nothing was done with them.
private void drawShapes(GraphicsContext gc) {
int r = 20;
while (r <= 80) {
gc.strokeOval(80-(r/2), 80, r, 60);
r = r + 10;
}
}
Related
I'm creating a basic animation with javaFX, I have 5 rectangles in a row, I have two cirlces, red and blue. The red cricle is set on the first rectangle and the blue one is set on the fifth rectangle.
The idea is: If I click the first rectangle I want the red circle to travel to the fifth rectangle (by a translation) and once it gets there the blue rectangle (which is on the fifth rectangle) travels to the first one, in another word they exchange positions. I used the AnimationTimer class in my logic but the porblem is that the animation of both cirlces is synchronized when the pressedMouse event happens and that's not what I want, what I want is that the animation of the blue circle starts once the red circle's one is finished. I want to understand why is that happening? Is it kinda multiple thread? In case it is, when I run the program the red circle gets stuck in the middle however the blue one travels out of the bounds and hides, however if I comment one of the cirlce's position code for update (the update method) the app runs correctly, I hope I get an answer and I'm so thankful.
Another question is: How to make my animation looks smoother because it stops for a fraction of a second and moves again.
here is my code:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class animation extends Application {
AnchorPane root = new AnchorPane();
//create a matrix from rectangle class to stock 5 rectangle objects
rectangle rect [] = new rectangle [5];
//isMoving gets the true value when the animation starts
private boolean isMoving = false;
private int traveledDistance = 30;
#Override
public void start(Stage primaryStage) {
//add 5 rectangles on the anchorpane
//rect[0], rect[2] and rect[4] have BURLYWOOD color
//rect[1], rect[3] have DARKBLUE color
for(int i = 0; i<5; i++)
{
if(i%2 == 0)
{
rect[i] = new rectangle();
rect[i].setFill(Color.BURLYWOOD);
}
else
{
rect[i] = new rectangle();
rect[i].setFill(Color.DARKBLUE);
}
//set all 5 rectangles as empty
rect[i].setRectEmpty(true);
//set all the 5 rectangles one after the other along the x axis
rect[i].setTranslateX(i*60);
//add the 5 rectangles to the parent
root.getChildren().add(rect[i]);
}
//instantiation of two circles (c and d) from cirlce class
circle c = new circle(Color.RED);
c.setName("redCircle");
circle d = new circle(Color.BLUE);
d.setName("blueCircle");
//set the position of the red circle centered relatively to rect[0]
//rect[0] is no longer empty as it contains the red circle
c.setTranslateX(30);
c.setTranslateY(30);
rect[0].setCircle(c);
rect[0].setCircleName(c.getName());
rect[0].setRectEmpty(false);
root.getChildren().add(c);
//set the position of the blue circle centered relatively to rect[4]
d.setTranslateX(4*60 +30);
d.setTranslateY(30);
rect[4].setCircle(d);
rect[4].setCircleName(d.getName());
root.getChildren().add(d);
displayedScene(primaryStage);
//when the parent is clicked
root.setOnMousePressed(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
//get the index of the clicked rectangle
int index = (int) event.getX()/60;
//if the clicked rectangle contains the red circle inside
if(!rect[index].isRectEmpty() && rect[index].getCircleName().equals("redCircle"))
{
Circle circle = rect[index].getCircle();
//update the postion of the red circle so that it occupies the last rectangle (rect[4])
update(index,5, circle);
//update the position of the blue circle so that it occupies the first rectangle(rect[0])
update(5,0, rect[4].getCircle());
}
}
});
}
//update method uses the AnimationTimer class
public void update(int initialPos, int lastPos, Circle circle)
{
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
updateCirclePosition(initialPos, lastPos, circle);
if(!isMoving)
{
this.stop();
}
}
};
timer.start();
}
public void updateCirclePosition(int initialPos, int lastPos, Circle circle)
{
int dx = 2;
if(initialPos>lastPos)
{
dx = -1*dx;
}
isMoving = true;
int distance = Math.abs((lastPos - initialPos)*60);
if(traveledDistance<distance-30)
{
circle.setTranslateX(circle.getTranslateX() + dx);
traveledDistance +=Math.abs(dx);
}
else{
isMoving = false;
traveledDistance = 30;
}
}
//load the Stage
public void displayedScene(Stage primaryStage)
{
Scene scene = new Scene(root, 300, 60);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
// circle class extends Circle
public class circle extends Circle
{
private String name;
public circle(Paint color) {
super(30, color);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
And here is rectangle class:
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
//rectangle class extends Rectangle
public class rectangle extends Rectangle {
private Circle circle;
private String circleName;
private boolean rectEmpty;
public rectangle() {
super(60, 60);
}
public Circle getCircle() {
return circle;
}
public void setCircle(Circle circle) {
this.circle = circle;
}
public boolean isRectEmpty() {
return rectEmpty;
}
public void setRectEmpty(boolean rectEmpty) {
this.rectEmpty = rectEmpty;
}
public String getCircleName() {
return circleName;
}
public void setCircleName(String circleName) {
this.circleName = circleName;
}
}
The following is an mre demonstrating the requested functionality.
Circles animation is done by animateCircles(). It uses TranslateTransition to translate a circle from one position to the other.
setOnFinished is used to start the next animation.
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Bounds;
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.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Animation extends Application {
private static final double SQUARE_SIZE = 60, RADIUS = SQUARE_SIZE /2, ANIMATION_TIME = 1;
private final Pane root = new Pane();
private final Rectangle rect [] = new Rectangle [5];
private final Circle circles [] = new Circle[2];
private boolean isMoving = false, isSwapped = false;
#Override
public void start(Stage primaryStage) {
for(int i = 0; i<rect.length; i++) {
rect[i] = new Rectangle(SQUARE_SIZE, SQUARE_SIZE, i%2 == 0 ? Color.BURLYWOOD : Color.DARKBLUE);
//set all the 5 rectangles one after the other along the x axis
rect[i].setTranslateX(i*SQUARE_SIZE);
root.getChildren().add(rect[i]);
}
circles[0] = new Circle(RADIUS,Color.RED);
circles[1] = new Circle(RADIUS,Color.BLUE);
//set the position of the red circle centered to rect[0]
Point2D center = centerOf(rect[0]);
circles[0].setTranslateX(center.getX());
circles[0].setTranslateY(center.getY());
//set the position of the blue circle centered to rect[4]
center = centerOf(rect[4]);
circles[1].setTranslateX(center.getX());
circles[1].setTranslateY(center.getY());
root.getChildren().add(circles[0]);
root.getChildren().add( circles[1]);
Scene scene = new Scene(root, SQUARE_SIZE*rect.length, SQUARE_SIZE);
primaryStage.setScene(scene);
primaryStage.show();
root.setOnMousePressed(event -> animateCircles());
}
//return the center point
private Point2D centerOf(Rectangle rect) {
Bounds bounds = rect.getBoundsInParent();
double x = bounds.getMinX() + 0.5 * bounds.getWidth();
double y = bounds.getMinY() + 0.5 * bounds.getHeight();
return new Point2D(x, y);
}
private void animateCircles() {
if(isMoving) return;
TranslateTransition translateCircle0 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[0]);
translateCircle0.setToX( isSwapped ? centerOf(rect[0]).getX() : centerOf(rect[4]).getX());
TranslateTransition translateCircle1 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[1]);
translateCircle1.setToX( isSwapped ? centerOf(rect[4]).getX() : centerOf(rect[0]).getX());
translateCircle0.setOnFinished(e-> {
translateCircle1.play();
});
translateCircle1.setOnFinished(e-> {
isMoving = false;
isSwapped = ! isSwapped;
});
isMoving = true;
translateCircle0.play();
}
public static void main(String[] args) {
launch(args);
}
}
Alternatively you could implement animateCircles() using SequentialTransition:
private void animateCircles() {
if(isMoving) return;
TranslateTransition translateCircle0 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[0]);
translateCircle0.setToX( isSwapped ? centerOf(rect[0]).getX() : centerOf(rect[4]).getX());
TranslateTransition translateCircle1 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[1]);
translateCircle1.setToX( isSwapped ? centerOf(rect[4]).getX() : centerOf(rect[0]).getX());
SequentialTransition sequentialTransition = new SequentialTransition(translateCircle0, translateCircle1);
isMoving = true;
sequentialTransition.play();
sequentialTransition.setOnFinished(e-> {
isMoving = false;
isSwapped = ! isSwapped;
});
}
I'm building a grid out of rectangles.
I want to click one of the rectangles and its color should change.
However, I dont know how to access the rectangles in Main AFTER they've been created.
Main:
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
public void changeColor(Pane p) {
p.setOnMouseClicked(me -> {
double posX = me.getX();
double posY = me.getY();
int colX = (int)(posX / 30);
int colY = (int) (posY / 30);
ObservableList<Node> children = p.getChildren();
for( Node d : children) {
if(d.getLayoutX() == colX && d.getLayoutY() == colY) {
// how can i access my rectangle here?
// basically, i want to be able to do .setFill()
}
}
});
}
#Override
public void start(Stage primaryStage) throws Exception {
Grid g = new Grid(30,30, 30);
Pane window = g.render();
Scene scene = new Scene(window, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
this.changeColor(window);
}
}
Grid:
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class Grid {
Integer width, height, squareSize;
Color fill = Color.ALICEBLUE,
stroke = Color.BLACK;
public Grid(int x, int y, int squareSize){
this.width = x;
this.height = y;
this.squareSize = squareSize;
}
public Pane render() {
Pane p = new Pane();
Rectangle [][] rect = new Rectangle[this.width][this.height];
for(int i = 0; i < this.width; i++) {
for(int j = 0; j < this.height; j++) {
rect[i][j] = new Rectangle();
rect[i][j].setX(i * width);
rect[i][j].setY(j * height);
rect[i][j].setWidth(this.squareSize);
rect[i][j].setHeight(this.squareSize);
rect[i][j].setFill(this.fill);
rect[i][j].setStroke(this.stroke);
p.getChildren().add(rect[i][j]);
}
}
return p;
}
}
Can someone please help me to figure out how I can access my rectangles again in the main file?
Keeping with your current design, you simply need to test if the mouse clicked within a child and if that child is an instance of Rectangle; then you can cast and call setFill. However, I recommend changing the name of changeColor as that name does not represent what that method is doing.
public void installChangeColorHandler(Pane pane) {
pane.setOnMouseClicked(event -> {
for (Node child : pane.getChildren()) {
if (child instanceof Rectangle
&& child.contains(child.parentToLocal(event.getX(), event.getY()))) {
((Rectangle) child).setFill(/* YOUR COLOR */);
event.consume();
break;
}
}
});
}
Since the event handler is added to Pane the x and y mouse coordinates are relative to said Pane. But since your Rectangles are direct children of Pane we can call Node.parentToLocal to transform those coordinates into the Rectangle's space1. We then need to test if the bounds of the Rectangle contain those coordinates using Node.contains; if it does, change the fill.
That said, you may want to modify your code so that you're adding an/the EventHandler directly to the Rectangles. That way you can use Event.getSource(). For instance2:
public Pane render(EventHandler<MouseEvent> onClick) {
// outer loop...
// inner loop...
Rectangle r = new Rectangle();
// configure r...
r.setOnMouseClicked(onClick);
// end inner loop...
// end outer loop...
}
...
// may want to consume event
Pane window = new Grid(30, 30, 30).render(event ->
((Rectangle) event.getSource()).setFill(/* YOUR COLOR */));
1. Even if they weren't direct children you can still transform the coordinates into the local space. For example, you can use Node.sceneToLocal and the scene coordinates provided by the MouseEvent (i.e. getSceneX()/getSceneY()).
2. This is still staying close to your design. You may want to rethink things, however, into a proper MVC (or other) architecture. Applying MVC With JavaFx
I have this code where I have random rectangles generating over the entire canvas. I also have 1 rectangle at the center.
I need to get the 1 rectangle at the center to move around when the user hovers the mouse over the canvas.
Here is my code
package sample;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.Random;
public class Main extends Application {
private static final int DRAW_WIDTH = 800;
private static final int DRAW_HEIGHT = 500;
private Animation myAnimation;
private Canvas canvas;
private GraphicsContext gtx;
#Override
public void start(Stage stage) throws Exception
{
stage.setTitle("tRIPPy BoXXX");
canvas = new Canvas(DRAW_WIDTH, DRAW_HEIGHT);
gtx = canvas.getGraphicsContext2D();
gtx.setLineWidth(3);
gtx.setFill(Color.BLACK);
gtx.fillRect(0, 0, DRAW_WIDTH, DRAW_HEIGHT);
VBox vBox = new VBox();
vBox.getChildren().addAll(canvas);
Scene scene = new Scene(vBox, DRAW_WIDTH, DRAW_HEIGHT);
stage.setScene(scene);
stage.show();
myAnimation = new Animation();
myAnimation.start();
}
class Animation extends AnimationTimer
{
#Override
public void handle(long now)
{
Random rand = new Random();
double red = rand.nextDouble();
double green = rand.nextDouble();
double blue = rand.nextDouble();
Color boxColor = Color.color(red,green,blue);
gtx.setStroke(boxColor);
....
This is the box that I want to have moving around with the users mouse. I have tried some things on my own, however I can't get the rect to stay the way it is in the code.
.....
int rectX = 800 / 2;
int rectY = 500 / 2;
for (int side = 10; side <= 100; side += 10) {
gtx.strokeRect(rectX - side / 2, rectY - side / 2, side, side);
}
int centerX = (int)(Math.random()*800);
int centerY = (int)(Math.random()*800);
for (int side = (int)(Math.random()*100); side <= Math.random()* 100; side += Math.random()*100)
{
gtx.strokeRect(centerX - side / 2, centerY - side / 2, side, side);
}
}
}
public static void main(String[] args)
{
launch(args);
}
If you plan to move some graphics arround, why do you then start with a canvas? On a canvas you cannot move anything arround unless you constantly want to redraw everything again and again. Putting your rectangles into the scene graph is much better suited for this task.
I'm currently developing a game in Java, and I've been trying to figure out how to draw a shape (e.g. a circle) to the canvas, on top of a different shape (e.g. a square), but to only draw the parts of the circle which are intersecting the square, similar to a clipping mask between layers in Photoshop.
I've tried using GraphicsContext.clearRect() to clear the areas where the bottom shape is not, but that removes the background.
The code below produces this result:
However, this is the result I desire:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class CircleWithinSquareTest extends Application {
#Override
public void start(Stage stage) throws Exception {
int width = 200;
int height = 200;
Canvas canvas = new Canvas(width, height);
GraphicsContext gc = canvas.getGraphicsContext2D();
AnimationTimer timer = new AnimationTimer() {
final int bgCellSize = 8;
final int x = 100;
final int y = 100;
double angle = 0;
#Override
public void handle(long now) {
/* Draw checkered background */
gc.setFill(Color.WHITE);
gc.fillRect(0, 0, width, height);
gc.setFill(Color.LIGHTGRAY);
boolean odd = false;
for (int y = 0; y < height; y += bgCellSize) {
odd = !odd;
for (int x = odd ? 0 : bgCellSize; x < width; x += bgCellSize * 2) {
gc.fillRect(x, y, bgCellSize, bgCellSize);
}
}
/* Draw square */
gc.setFill(Color.BLUE);
gc.fillRect(x, y, 50, 50);
/* Draw circle */
gc.save();
angle += 5;
if (angle >= 360) {
angle = 0;
}
Rotate r = new Rotate(angle, x, y);
gc.setTransform(r.getMxx(), r.getMyx(), r.getMxy(), r.getMyy(), r.getTx(), r.getTy());
gc.setFill(Color.RED);
gc.fillOval(x, y, 30, 30);
gc.restore();
}
};
timer.start();
Group root = new Group(canvas);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
You can use clipping, add next code before setTransform:
gc.beginPath();
gc.rect(x, y, 50, 50);
gc.closePath();
gc.clip();
I recently wanted to create an animated background in JavaFX, similar to the Swing example seen here. I used a Canvas on which to draw, as shown in Working with the Canvas API, and an AnimationTimer for the drawing loop, as shown in Animation Basics. Unfortunately, I'm not sure how to resize the Canvas automatically as the enclosing Stage is resized. What is a good approach?
A similar question is examined in How to make canvas Resizable in javaFX?, but the accepted answer there lacks the binding illustrated in the accepted answer here.
In the example below, the static nested class CanvasPane wraps an instance of Canvas in a Pane and overrides layoutChildren() to make the canvas dimensions match the enclosing Pane. Note that Canvas returns false from isResizable(), so "the parent cannot resize it during layout," and Pane "does not perform layout beyond resizing resizable children to their preferred sizes." The width and height used to construct the canvas become its initial size. A similar approach is used in the Ensemble particle simulation, FireworksApp, to scale a background image while retaining its aspect ratio.
As an aside, note the difference from using fully saturated colors compared to the original. These related examples illustrate placing controls atop the animated background.
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/31761362/230513
* #see https://stackoverflow.com/a/8616169/230513
*/
public class Baubles extends Application {
private static final int MAX = 64;
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private static final Random RND = new Random();
private final Queue<Bauble> queue = new LinkedList<>();
private Canvas canvas;
#Override
public void start(Stage stage) {
CanvasPane canvasPane = new CanvasPane(WIDTH, HEIGHT);
canvas = canvasPane.getCanvas();
BorderPane root = new BorderPane(canvasPane);
CheckBox cb = new CheckBox("Animate");
cb.setSelected(true);
root.setBottom(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
for (int i = 0; i < MAX; i++) {
queue.add(randomBauble());
}
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext g = canvas.getGraphicsContext2D();
g.setFill(Color.BLACK);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
for (Bauble b : queue) {
g.setFill(b.c);
g.fillOval(b.x, b.y, b.d, b.d);
}
queue.add(randomBauble());
queue.remove();
}
};
loop.start();
cb.selectedProperty().addListener((Observable o) -> {
if (cb.isSelected()) {
loop.start();
} else {
loop.stop();
}
});
}
private static class Bauble {
private final double x, y, d;
private final Color c;
public Bauble(double x, double y, double r, Color c) {
this.x = x - r;
this.y = y - r;
this.d = 2 * r;
this.c = c;
}
}
private Bauble randomBauble() {
double x = RND.nextDouble() * canvas.getWidth();
double y = RND.nextDouble() * canvas.getHeight();
double r = RND.nextDouble() * MAX + MAX / 2;
Color c = Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
return new Bauble(x, y, r, c);
}
private static class CanvasPane extends Pane {
private final Canvas canvas;
public CanvasPane(double width, double height) {
canvas = new Canvas(width, height);
getChildren().add(canvas);
}
public Canvas getCanvas() {
return canvas;
}
#Override
protected void layoutChildren() {
super.layoutChildren();
final double x = snappedLeftInset();
final double y = snappedTopInset();
// Java 9 - snapSize is deprecated, use snapSizeX() and snapSizeY() accordingly
final double w = snapSize(getWidth()) - x - snappedRightInset();
final double h = snapSize(getHeight()) - y - snappedBottomInset();
canvas.setLayoutX(x);
canvas.setLayoutY(y);
canvas.setWidth(w);
canvas.setHeight(h);
}
}
public static void main(String[] args) {
launch(args);
}
}
I combined both prior solutions ( #trashgod and #clataq's ) by putting the canvas in a Pane and binding it to it:
private static class CanvasPane extends Pane {
final Canvas canvas;
CanvasPane(double width, double height) {
setWidth(width);
setHeight(height);
canvas = new Canvas(width, height);
getChildren().add(canvas);
canvas.widthProperty().bind(this.widthProperty());
canvas.heightProperty().bind(this.heightProperty());
}
}
Couldn't you do this with a Binding as well? The following seems to produce the same results without having to add the derived class.
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.DoubleBinding;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* #see http://stackoverflow.com/a/31761362/230513
* #see http://stackoverflow.com/a/8616169/230513
*/
public class Baubles extends Application {
private static final int MAX = 64;
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private static final Random RND = new Random();
private final Queue<Bauble> queue = new LinkedList<>();
private Canvas canvas;
#Override
public void start(Stage stage) {
canvas = new Canvas(WIDTH, HEIGHT);
BorderPane root = new BorderPane(canvas);
CheckBox cb = new CheckBox("Animate");
cb.setSelected(true);
root.setBottom(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
// Create bindings for resizing.
DoubleBinding heightBinding = root.heightProperty()
.subtract(root.bottomProperty().getValue().getBoundsInParent().getHeight());
canvas.widthProperty().bind(root.widthProperty());
canvas.heightProperty().bind(heightBinding);
for (int i = 0; i < MAX; i++) {
queue.add(randomBauble());
}
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext g = canvas.getGraphicsContext2D();
g.setFill(Color.BLACK);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
for (Bauble b : queue) {
g.setFill(b.c);
g.fillOval(b.x, b.y, b.d, b.d);
}
queue.add(randomBauble());
queue.remove();
}
};
loop.start();
cb.selectedProperty().addListener((Observable o) -> {
if (cb.isSelected()) {
loop.start();
} else {
loop.stop();
}
});
}
private static class Bauble {
private final double x, y, d;
private final Color c;
public Bauble(double x, double y, double r, Color c) {
this.x = x - r;
this.y = y - r;
this.d = 2 * r;
this.c = c;
}
}
private Bauble randomBauble() {
double x = RND.nextDouble() * canvas.getWidth();
double y = RND.nextDouble() * canvas.getHeight();
double r = RND.nextDouble() * MAX + MAX / 2;
Color c = Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
return new Bauble(x, y, r, c);
}
public static void main(String[] args) {
launch(args);
}
}