Here is my following code:
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Scene scene = new Scene(root, 500, 600);
Random rand = new Random();
int random = rand.nextInt(100-5)+5;
Circle circle = new Circle(0, 10, random);
circle.setFill(Color.RED);
root.getChildren().add(circle);
Timeline timeline = new Timeline(
new KeyFrame(
Duration.seconds(2),
new KeyValue(circle.translateXProperty(), 600)
));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.setAutoReverse(false);
timeline.play();
primaryStage.setScene(scene);
primaryStage.show();
}
In this code it only update the random value once, but I want it to update random value after each cycle. Is there any way I can update my random value after each cycle? Any help is appreciated!
If you're using the Timeline
It is not possible to change the keyFrames of a running Timeline. If
the value of keyFrames is changed for a running Timeline, it has to be
stopped and started again to pick up the new value.
So, you can recreate the timeline everytime you want to create a new keyFrame, in your case with a new Circle(0, 10, new Random().nextInt(100-5)+5, Color.RED);
There is a constructor for Keyframe which takes multiple KeyValue. You can add to the constructor a new KeyValue with the radius of circle. As far as I can see you should create your own implementation of Interpolator to make alive the random behaviour for the radius. (Maybe there are third party solutions for that)
You should add a EventHandler<ActionEvent> to your call to the KeyValue constructor.
That is, add a parameter to new KeyValue(circle.translateXProperty(), 600) so it looks something like new KeyValue(circle.translateXProperty(), (event)->{circle.setRadius(rand.nextInt(100-5)+5);}, 600)
The default Interpolater(EASE_BOTH) clamps the value of time for current interval i.e. frac value will range from [0.0, ... , 1.0]. You can use the same information and update the radius at the beginning of the new cycle.
public void start(Stage primaryStage) {
Pane root = new Pane();
Scene scene = new Scene(root, 500, 600);
final Random random = new Random();
Circle circle = new Circle(0, 0, random.nextInt(100));
circle.setFill(Color.RED);
root.getChildren().add(circle);
Animation animation = new Transition() {
{
setCycleCount(INDEFINITE);
setCycleDuration(Duration.seconds(2));
}
#Override
protected void interpolate(double frac) {
if (frac == 0) {
final int newRadius = random.nextInt(100);
System.out.println("New Radius: " + newRadius);
circle.setRadius(newRadius + 5);
}
circle.setTranslateX(frac * 600);
}
};
animation.play();
primaryStage.setScene(scene);
primaryStage.show();
}
Add a EventHandler<ActionEvent> that updates the radius property to the last KeyFrame of the Timeline:
final Random rand = new Random();
int random = rand.nextInt(100-5)+5;
final Circle circle = new Circle(0, 10, random);
circle.setFill(Color.RED);
root.getChildren().add(circle);
Timeline timeline = new Timeline(
new KeyFrame(
Duration.seconds(2),
event -> circle.setRadius(rand.nextInt(100-5)+5),
new KeyValue(circle.translateXProperty(), 600)
));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.setAutoReverse(false);
timeline.play();
Not sure if this will slow down your code, but you might want to try setting cycle count to 1 instead of Animation.INDEFINITE and wrapping the entire block in a while loop:
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Scene scene = new Scene(root, 500, 600);
Random rand = new Random();
primaryStage.setScene(scene);
primaryStage.show();
while (true) {
int random = rand.nextInt(100-5)+5;
Circle circle = new Circle(0, 10, random);
circle.setFill(Color.RED);
root.getChildren().add(circle);
Timeline timeline = new Timeline(new KeyFrame(
Duration.seconds(2),
new KeyValue(circle.translateXProperty(), 600)
));
timeline.setCycleCount(1);
timeline.setAutoReverse(false);
timeline.play();
}
}
One issue is that the primaryStage.show() will get moved up (so that it still runs despite the forever loop). Consider changing the while (true) to something else if this becomes a problem.
Related
I have seen this timeline build here then I have tried to make I timeline using hbox and sperators and labels, but I fall in a representation errors
the following controller allows me to build this timeline this above nodes
public class GuiMainController implements Initializable {
#FXML
private BorderPane borderPane;
public void getUnit1(){
//This will fill the Time Line Unit in your Gui
VBox vbox = new VBox();
HBox hbox = new HBox();
hbox.setManaged(false);
for (int i =0; i < 24; i++) {
//For each unit create a new instance
AnchorPane anchorPane = new AnchorPane();
anchorPane.setPrefHeight(30);
anchorPane.setPrefWidth(66);
Separator separatorR = new Separator();
separatorR.setLayoutX(66);
separatorR.setLayoutY(14);
separatorR.setOrientation(Orientation.VERTICAL);
separatorR.setPrefHeight(15);
Separator separatorM = new Separator();
separatorM.setLayoutY(14);
separatorM.setOrientation(Orientation.VERTICAL);
separatorM.setPrefHeight(15);
Separator separatorL = new Separator();
separatorL.setLayoutX(33);
separatorL.setLayoutY(20);
separatorL.setOrientation(Orientation.VERTICAL);
separatorL.setPrefHeight(10);
separatorL.setPrefWidth(6);
Label lblT = new Label();
lblT.setText(i+"h");
Label lblTT = new Label();
lblTT.setLayoutX(66);
lblTT.setText((i+1)+"h");
anchorPane.getChildren().addAll(separatorL,lblT,separatorM,separatorR,lblTT);
hbox.getChildren().add(anchorPane);
}
borderPane.getChildren().add(hbox);
}
public void initialize(URL arg0, ResourceBundle arg1) {
getUnit1();
}
}
How can I made this timeline, any help are appreciated to get some like this
Imho you shouldn't get any layouts involved here, since this way you rely on the exact implementation of those layouts. Furthermore I'm not sure Seperator should be involved here. You could use a Path though and calculate the positions of all the lines on the axis.
private static Label createLabel(String text, double layoutY, double majorTickDistance, int index) {
Label label = new Label(text);
// label width should be exactly the distance between ticks
label.setMaxWidth(Region.USE_PREF_SIZE);
label.setMinWidth(Region.USE_PREF_SIZE);
label.setPrefWidth(majorTickDistance);
// center text
label.setAlignment(Pos.BASELINE_CENTER);
label.setLayoutY(layoutY);
// align center of the label with major tick
label.setLayoutX((index + 0.5) * majorTickDistance);
return label;
}
/**
* #param majorTickDistance the width between major ticks
* #param minorTicks the number of minor ticks between major ticks
* #param majorTickHeight the height of major tick markers
* #param minorTickHeight the height of minor tick markers
* #param firstText the first text for the first major tick
* #param text the text for other ticks
*/
private static Pane createAxis(double majorTickDistance, int minorTicks,
double majorTickHeight, double minorTickHeight, String firstText, String... text) {
final double labelY = majorTickHeight + 3;
final double minorTickDistance = majorTickDistance / minorTicks;
// initialize path with first major tick and horizontal line
Path path = new Path(
new MoveTo(0, 0), new HLineTo(majorTickDistance * text.length),
new MoveTo(0, 0), new VLineTo(majorTickHeight)
);
// add path and first label
Pane pane = new Pane(path, createLabel(firstText, labelY, majorTickDistance, -1));
for (int i = 0; i < text.length; i++) {
double offset = i * majorTickDistance;
// add minor ticks
for (int j = 1; j < minorTicks; j++) {
double x = offset + j * minorTickDistance;
path.getElements().addAll(new MoveTo(x, 0), new VLineTo(minorTickHeight));
}
offset += majorTickDistance;
// add major tick at the end of the current interval
path.getElements().addAll(new MoveTo(offset, 0), new VLineTo(majorTickHeight));
// add label below major tick
pane.getChildren().add(createLabel(text[i], labelY, majorTickDistance, i));
}
pane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
return pane;
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(new StackPane(createAxis(30, 5, 10, 5, "1", "2", "3", "4", "5", "6")), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
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();
}
I created a program that randomly draws circles,squares,rectangles. My problem is I'm getting the click position with event.getX() and event.getY();
Here is the problem:
Green point is the where I click the Canvas.
↑→ axis must be like this. But in my program it stands like this ↓→
Here is the details:
Getting mouse position with (extending Canvas):
super.setOnMouseClicked(event -> selectFigureContaining(event.getX(),event.getY()));
Initializing part :
#Override
public void start(Stage primaryStage) {
this.color = Color.BLACK;
toggleGroup = new ToggleGroup();
hBox = new HBox();
vBox = new VBox();
label = new Label();
vBox.setPadding(new Insets(5,15,0,15));
vBox.setSpacing(10);
hBox.setPadding(new Insets(5,0,0,0));
//Radio Buttons
red = new RadioButton("Red");
red.setSelected(true);
green = new RadioButton("Green");
blue = new RadioButton("Blue");
red.setToggleGroup(toggleGroup);
green.setToggleGroup(toggleGroup);
blue.setToggleGroup(toggleGroup);
//Buttons
buttonCircle = new Button("Circle");
buttonCircle.setMinSize(70,20);
buttonCircle.setOnMouseClicked(event -> circleButtonClicked());
buttonSquare = new Button("Square");
buttonSquare.setMinSize(70,20);
buttonSquare.setOnMouseClicked(event -> squareButtonClicked());
buttonRectangle = new Button("Rectangle");
buttonRectangle.setMinSize(70,20);
buttonRectangle.setOnMouseClicked(event -> rectangleButtonClicked());
cleanAll = new Button("Clean All");
cleanAll.setMinSize(70,20);
cleanAll.setOnMouseClicked(event -> cleanAllEvent());
figureCanvas = new FigureCanvas(10,400,500, this);
vBox.getChildren().addAll(red,green,blue,buttonCircle,buttonSquare,buttonRectangle,cleanAll,label);
hBox.getChildren().addAll(vBox,figureCanvas);
Scene scene = new Scene(hBox,500,500);
primaryStage.setScene(scene);
primaryStage.setTitle("Draw Figure");
primaryStage.show();
}
I'm really new to JavaFX. I couldn't understand why y axis getting smaller on left upper side. Thanks.
Normally, the coordinate system's origin is at the top left of the screen and positive y is downwards:
. ---> +x
|
v +y
You can transform your y position like this to get expected movement (origin at the bottom left, +y goes up):
y = canvas.getHeight() - y;
I have a polygon that I want to grow in size when the window is resized. I have a DoubleBinding that it the baseline for the size of all of my shapes in the scene. I use this DoubleBinding to create a ListBinding that calculates how big the polygon should be. I tried to use Bindings.bindContent(list1, list2) to bind the ListBinding to the polygon's points, but it doesn't seem to work.
Polygon triangle = new Polygon();
ListBinding<Double> trianglePoints = new ListBinding<Double>() {
{
super.bind(width, height);
}
#Override
protected ObservableList<Double> computeValue() {
ObservableList<Double> points = FXCollections.observableArrayList();
points.addAll(0.0, 0.0, width.get()/2, height.get()/4, 0.0, height.get()/2);
return points;
}
};
Bindings.bindContent(triangle.getPoints(), trianglePoints.get());
I found that if I add an InvalidationListener to the triangle points and manually change the the polygon points when it fires off, it works.
trianglePoints.addListener((InvalidationListener)(listener) -> triangle.getPoints().setAll(trianglePoints.get()));
What am I doing wrong?
You should change your binding in following way:
Bindings.bindContent(triangle.getPoints(), trianglePoints);
Your previous binding did not work because trianglePoints.get() is just result of computeValue() call i.e. creating ObservableList with some values calculated upon initial width and height. Thus further changes to width and height properties were not taken into account.
I recommend not using a ListBinding. You do not need to recreate the list over and over again, you just need to modify some of the points:
#Override
public void start(Stage primaryStage) {
Polygon triangle = new Polygon(
0, 0,
0, 0,
0, 0);
StackPane root = new StackPane();
root.getChildren().add(triangle);
root.heightProperty().addListener((observable, oldH, newH) -> {
double h = newH.doubleValue();
triangle.getPoints().set(3, h / 4);
triangle.getPoints().set(5, h / 2);
});
root.widthProperty().addListener((observable, oldW, newW) -> {
double w = newW.doubleValue();
triangle.getPoints().set(2, w / 2);
});
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
Furthermore it's also possible to do this using the scale properties (unless you're using a stroke or some exotic fill):
#Override
public void start(Stage primaryStage) {
Polygon triangle = new Polygon(
0, 0,
0.5, 0.25,
0, 0.5);
StackPane root = new StackPane();
root.getChildren().add(triangle);
triangle.scaleXProperty().bind(root.widthProperty());
triangle.scaleYProperty().bind(root.heightProperty());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
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); }
}