Язык программирования:
Java
Среда программирования:
IntelliJ IDEA 15.0.4
package me.grafika.barbanyagra; import javafx.animation.Animation; import javafx.animation.Interpolator; import javafx.animation.ParallelTransition; import javafx.animation.PathTransition; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.image.ImageView; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; import javafx.scene.shape.*; import javafx.stage.Stage; import javafx.util.Duration; public class SplittingDisks extends Application { private final double R = 24; // радиус одного круга private final double SINGLE_CYCLE_MS = 1100; // время выполнения одного цикла анимации public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) throws Exception { Group root = new Group(); // корневой элемент сцены primaryStage.setTitle("Splitting Disks"); // заголовок окна primaryStage.setScene(new Scene(root, 500, 500, Color.WHITE)); primaryStage.setResizable(false); // запрещаем изменять размер окна // чтобы круги касались, центры 3-х соседних гругов должны образовывать равносторонний треугольник со сторонами 2*R // тогда высота находится как 2 * R * sqrt(3) / 2 = sqrt(3) * R double h = Math.round(Math.sqrt(3) * R); // считаем достаточную длину полоски int columns = (int) Math.ceil(1 + 250 / R + (250 - h / 2) / h + 1); // честно строим одну полоску Shape basic = makeStrip(R, columns, h); double w = basic.getLayoutBounds().getWidth(); // сохраняем ее ширину // а дальше будем работать с ее снимком (потому что так быстрее) WritableImage image = basic.snapshot(null, null); Group imageViews = new Group(); // сюда сохраним все полоски ParallelTransition pt = new ParallelTransition(); // а сюда -- все перемещения, чтобы затем одновременно их запустить // размещаем центральную полоску (которая не двигается) ImageView iv = new ImageView(image); iv.relocate(-R, 250 - h / 2); imageViews.getChildren().add(iv); // описываем перемещения полосок выше центральной double y = 250 - h / 2 - h, dx = 2 * R; for (int d = 1; y + h > 0; d++, y -= h, dx += 2 * R) { iv = new ImageView(image); // создаем полоску double x = d % 2 == 1 ? -2 * R : -R; // определяем смещение по четности imageViews.getChildren().add(iv); // добавляем в групу PathTransition transition = new PathTransition( // создаем перемещение Duration.millis(SINGLE_CYCLE_MS), // указываем время выполнения перемещения new Path( // перемещение описывается относительно центра фигуры, поэтому необходимы смещения на w/2 и h/2 new MoveTo(w - w / 2 + x, h / 2 + y), new HLineTo(w - w / 2 + x - dx) ), iv // указываем полоску, которую необходимо перемещать ); transition.setInterpolator(Interpolator.EASE_BOTH); // указываем нелинейный интерполятор pt.getChildren().add(transition); // сохраняем перемещение } // аналогично описываем перемещения полосок ниже центральной y = 250 + h / 2; dx = 2 * R; for (int d = 1; y < 500; d++, y += h, dx += 2 * R) { System.out.println(d); iv = new ImageView(image); double x = d % 2 == 1 ? -2 * R : -R; imageViews.getChildren().add(iv); PathTransition transition = new PathTransition( Duration.millis(SINGLE_CYCLE_MS), new Path( new MoveTo(w / 2 + x - dx, h / 2 + y), new HLineTo(w / 2 + x) ), iv ); transition.setInterpolator(Interpolator.EASE_BOTH); pt.getChildren().add(transition); } root.getChildren().add(imageViews); // добавляем полоски primaryStage.show(); // запускаем сцену pt.setCycleCount(Animation.INDEFINITE); // указываем, что перемещение должно выполняться неограниченное число раз pt.play(); // запускаем анимацию } // функция, строящая одну полоску высотой h из 2 * columns плотнорасположенных полуокружностей радиуса r private Shape makeStrip(double r, int columns, double h) { // создаем круг и прямугольник Circle circle = new Circle(r); Rectangle rectangle = new Rectangle(2 * r, r); circle.relocate(0, -r); Shape upResult = Shape.intersect(rectangle, circle); // пересечением находим "нижнюю" полуокружность circle.relocate(0, 0); Shape downResult = Shape.intersect(rectangle, circle); // пересечением находим "верхнюю" полуокружность Shape result = new Circle(); // любая пустая фигура for (int i = 0; i < columns; i++) { upResult.relocate(i * 2 * r, 0); result = Shape.union(result, upResult); // добавляем "нижнюю" полуокружность downResult.relocate(-r + i * 2 * r, h - r); result = Shape.union(result, downResult); // добавляем "верхнюю" полуокружность } return result; } }
Прикрепленный файл | Размер |
---|---|
SplittingDisks.zip | 35.98 кб |