package com.weiqigao.stlouisjug.shapesandpaths; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.animation.TimelineBuilder; import javafx.application.Application; import javafx.beans.binding.DoubleBinding; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.scene.GroupBuilder; import javafx.scene.Scene; import javafx.scene.SceneBuilder; import javafx.scene.control.Slider; import javafx.scene.control.SliderBuilder; import javafx.scene.layout.HBox; import javafx.scene.layout.HBoxBuilder; import javafx.scene.layout.Priority; import javafx.scene.layout.VBoxBuilder; import javafx.scene.paint.Color; import javafx.scene.shape.LineBuilder; import javafx.scene.shape.LineTo; import javafx.scene.shape.MoveTo; import javafx.scene.shape.Path; import javafx.scene.text.TextBuilder; import javafx.stage.Stage; import javafx.util.Duration; /** * @author Weiqi Gao */ public class LissajousCurveExample extends Application { DoubleProperty a = new SimpleDoubleProperty(2.0); DoubleProperty b = new SimpleDoubleProperty(3.0); DoubleProperty delta = new SimpleDoubleProperty(Math.PI); public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage stage) throws Exception { stage.setTitle("Lissajous Curve Example"); stage.setScene(makeScene()); stage.show(); System.out.println("stage.getWidth() = " + stage.getWidth()); System.out.println("stage.getHeight() = " + stage.getHeight()); } private Scene makeScene() { Slider aSlider; Slider bSlider; Slider deltaSlider; Scene scene = SceneBuilder.create() .root(VBoxBuilder.create() .padding(new Insets(10, 10, 10, 10)) .spacing(10) .children( GroupBuilder.create() .children( LineBuilder.create() .startX(-240.0) .startY(0.0) .endX(240.0) .endY(0.0) .stroke(Color.RED) .build(), LineBuilder.create() .startX(0.0) .startY(-240.0) .endX(0.0) .endY(240.0) .stroke(Color.RED) .build(), createPath() ) .scaleY(-1) .build(), HBoxBuilder.create() .spacing(10) .children( TextBuilder.create() .text("a") .build(), aSlider = SliderBuilder.create() .min(1) .max(12) .value(2) .majorTickUnit(1) .minorTickCount(0) .snapToTicks(true) .showTickMarks(true) .showTickLabels(true) .build()) .build(), HBoxBuilder.create() .spacing(10) .children( TextBuilder.create() .text("b") .build(), bSlider = SliderBuilder.create() .min(1) .max(12) .value(3) .majorTickUnit(1) .minorTickCount(0) .snapToTicks(true) .showTickMarks(true) .showTickLabels(true) .build()) .build(), HBoxBuilder.create() .spacing(10) .children( TextBuilder.create() .text("delta") .build(), deltaSlider = SliderBuilder.create() .min(0.0) .max(2 * Math.PI) .value(Math.PI) .majorTickUnit(Math.PI / 12) .minorTickCount(0) .showTickMarks(true) .showTickLabels(true) .build() ) .build() ) .build()) .build(); HBox.setHgrow(aSlider, Priority.ALWAYS); HBox.setHgrow(bSlider, Priority.ALWAYS); HBox.setHgrow(deltaSlider, Priority.ALWAYS); aSlider.valueProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observableValue, Number oldValue, Number newValue) { TimelineBuilder.create() .keyFrames( new KeyFrame(Duration.seconds(0), new KeyValue(a, oldValue.intValue())), new KeyFrame(Duration.seconds(3), new KeyValue(a, newValue.intValue())) ) .build().play(); } }); bSlider.valueProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observableValue, Number oldValue, Number newValue) { TimelineBuilder.create() .keyFrames( new KeyFrame(Duration.seconds(0), new KeyValue(b, oldValue.intValue())), new KeyFrame(Duration.seconds(3), new KeyValue(b, newValue.intValue())) ) .build().play(); } }); deltaSlider.valueProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observableValue, Number oldValue, Number newValue) { TimelineBuilder.create() .keyFrames( new KeyFrame(Duration.seconds(0), new KeyValue(delta, oldValue.doubleValue())), new KeyFrame(Duration.seconds(1), new KeyValue(delta, newValue.doubleValue())) ) .build().play(); } }); return scene; } private Path createPath() { int points = 360; double increment = 2 * Math.PI / points; Path path = new Path(); for (int i = 0; i <= 360; i++) { final double t = i * increment; if (i == 0) { MoveTo moveTo = new MoveTo(200 * Math.sin(a.get() * t + delta.get()), 200 * Math.sin(b.get() * t)); moveTo.xProperty().bind(new DoubleBinding() { { super.bind(a, b, delta); } @Override protected double computeValue() { return 200 * Math.sin(a.get() * t + delta.get()); } }); moveTo.yProperty().bind(new DoubleBinding() { { super.bind(a, b, delta); } @Override protected double computeValue() { return 200 * Math.sin(b.get() * t); } }); path.getElements().add(moveTo); } else { LineTo lineTo = new LineTo(200 * Math.sin(a.get() * t + delta.get()), 200 * Math.sin(b.get() * t)); lineTo.xProperty().bind(new DoubleBinding() { { super.bind(a, b, delta); } @Override protected double computeValue() { return 200 * Math.sin(a.get() * t + delta.get()); } }); lineTo.yProperty().bind(new DoubleBinding() { { super.bind(a, b, delta); } @Override protected double computeValue() { return 200 * Math.sin(b.get() * t); } }); path.getElements().add(lineTo); } } path.setStroke(Color.BLUE); path.setStrokeWidth(3); return path; } }