【问题标题】:JavaFX 8 3D animationJavaFX 8 3D 动画
【发布时间】:2017-04-04 22:00:47
【问题描述】:

我正在制作一个小的 JavaFX 8 项目。我需要一些建议如何正确地制作动画。关于项目的一些事情。这是一个程序,它通过磁场动画带电粒子的流动。所有需要的值都取自用户将它们放入文本字​​段的 GUI。单击按钮后,我们将转移到 3D 场景,其中我的点显示为 Sphere,所有值都已设置。并且字段线打印在它的目录中。

问题是如何做propper动画。我试图使用我的 Sphere X Y Z 坐标,但找不到任何设置这些坐标的方法。 Z平面的运动应该是线性的,速度相同。 XY 平面中的运动应该是圆形的。我可以使用路径转换吗?

我的愿景是创建通过路径绘制球体的刻度动画。再打勾,下一个球体将使用由平移向量计算的新坐标绘制。

【问题讨论】:

    标签: java animation javafx 3d


    【解决方案1】:

    这可以使用PathTransition 吗?不,javafx 中的路径是 2 维的,但您需要 3D 运动。

    请注意,球坐标也不是描述这种运动的好坐标系,因为角度的计算有点复杂。

    更适合的坐标系是圆柱体坐标。

    您可以使用多个变换并使用 Timeline 动画为它们设置动画来实现这种运动:

    private static void animateSphere(Sphere sphere) {
        Rotate rot = new Rotate();
        Translate radiusTranslate = new Translate(50, 0, 0);
        Translate zMovement = new Translate();
    
        sphere.getTransforms().setAll(zMovement, rot, radiusTranslate);
        Timeline tl = new Timeline(
                new KeyFrame(Duration.ZERO,
                             new KeyValue(zMovement.zProperty(), 0d),
                             new KeyValue(rot.angleProperty(), 0d)),
                new KeyFrame(Duration.seconds(4),
                             new KeyValue(zMovement.zProperty(), 900d, Interpolator.LINEAR),
                             new KeyValue(rot.angleProperty(), 720, Interpolator.LINEAR))
        );
        tl.setCycleCount(Timeline.INDEFINITE);
        tl.play();
    }
    
    @Override
    public void start(Stage primaryStage) {
        Sphere sphere = new Sphere(30);
    
        Pane root = new Pane(sphere);
    
        Scene scene = new Scene(root, 400, 400, true);
        PerspectiveCamera camera = new PerspectiveCamera();
        camera.setTranslateZ(-10);
        camera.setTranslateX(-500);
        camera.setTranslateY(-200);
        camera.setRotationAxis(new Point3D(0, 1, 0));
        camera.setRotate(45);
        scene.setCamera(camera);
    
        animateSphere(sphere);
    
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    

    您的运动是螺旋运动,因此以下变换组合将适当地移动Sphere

    1. 按半径平移(z 分量 0)
    2. 旋转到合适的角度
    3. 沿 z 方向平移

    注意:变换应用的顺序与它们在 transforms 列表中出现的顺序相反。


    或者,您可以编写一个可与​​圆柱坐标参数一起使用的助手,并编写适当的 xyz 值:

    public class CylinderCoordinateAdapter {
    
        private final DoubleProperty theta = new SimpleDoubleProperty();
        private final DoubleProperty radius = new SimpleDoubleProperty();
        private final DoubleProperty h = new SimpleDoubleProperty();
    
        private static final Point3D DEFAULT_AXIS = new Point3D(0, 0, 1);
        private Point3D axis2;
        private Point3D axis3;
    
        private final ObjectProperty<Point3D> axis = new SimpleObjectProperty<Point3D>() {
    
            @Override
            public void set(Point3D newValue) {
                newValue = (newValue == null || newValue.equals(Point3D.ZERO)) ? DEFAULT_AXIS : newValue.normalize();
    
                // find first value ortogonal to axis with z = 0
                axis2 = newValue.getX() == 0 && newValue.getY() == 0 ? new Point3D(1, 0, 0) : new Point3D(-newValue.getY(), newValue.getX(), 0).normalize();
    
                // find axis ortogonal to the other 2
                axis3 = newValue.crossProduct(axis2);
                super.set(newValue);
            }
        };
    
        public CylinderCoordinateAdapter(WritableValue<Number> x, WritableValue<Number> y, WritableValue<Number> z) {
            Objects.requireNonNull(x);
            Objects.requireNonNull(y);
            Objects.requireNonNull(z);
            axis.set(DEFAULT_AXIS);
            InvalidationListener listener = o -> {
                Point3D ax = axis.get();
                double h = getH();
                double theta = getTheta();
                double r = getRadius();
    
                Point3D endPoint = ax.multiply(h).add(axis2.multiply(Math.cos(theta) * r)).add(axis3.multiply(Math.sin(theta) * r));
    
                x.setValue(endPoint.getX());
                y.setValue(endPoint.getY());
                z.setValue(endPoint.getZ());
            };
            theta.addListener(listener);
            radius.addListener(listener);
            h.addListener(listener);
            axis.addListener(listener);
    
            listener.invalidated(null);
        }
    
        public final Point3D getAxis() {
            return this.axis.get();
        }
    
        public final void setAxis(Point3D value) {
            this.axis.set(value);
        }
    
        public final ObjectProperty<Point3D> axisProperty() {
            return this.axis;
        }
    
        public final double getH() {
            return this.h.get();
        }
    
        public final void setH(double value) {
            this.h.set(value);
        }
    
        public final DoubleProperty hProperty() {
            return this.h;
        }
    
        public final double getRadius() {
            return this.radius.get();
        }
    
        public final void setRadius(double value) {
            this.radius.set(value);
        }
    
        public final DoubleProperty radiusProperty() {
            return this.radius;
        }
    
        public final double getTheta() {
            return this.theta.get();
        }
    
        public final void setTheta(double value) {
            this.theta.set(value);
        }
    
        public final DoubleProperty thetaProperty() {
            return this.theta;
        }
    
    }
    
    private static void animateSphere(Sphere sphere) {
         CylinderCoordinateAdapter adapter = new CylinderCoordinateAdapter(
                sphere.translateXProperty(),
                sphere.translateYProperty(),
                sphere.translateZProperty());
    
        adapter.setRadius(50);
    
        Timeline tl = new Timeline(
                new KeyFrame(Duration.ZERO,
                             new KeyValue(adapter.hProperty(), 0d),
                             new KeyValue(adapter.thetaProperty(), 0d)),
                new KeyFrame(Duration.seconds(4),
                             new KeyValue(adapter.hProperty(), 900d, Interpolator.LINEAR),
                             new KeyValue(adapter.thetaProperty(), Math.PI * 4, Interpolator.LINEAR))
        );
        tl.setCycleCount(Timeline.INDEFINITE);
        tl.play();
    }
    

    【讨论】:

    • 非常感谢!你会在这里回答我的其他问题吗?
    • @LucasPG 如果对此答案有疑问,请写评论。如果是新问题:我通常每天回答几个问题。如果你写一个问题,它写得很好,很有趣,没有足够质量的答案,我知道答案,我可能会发布它......但是周围也有其他非常有能力的用户可以回答你的问题.. .
    • 你好。在我的应用程序中,用户输入了所有需要的值。我计算了圆周运动的半径,也计算了 XY 平面上的速度和通过 Z 轴的速度。如何将所有这 3 个值放入您的 animateSphere 函数以使移动与用户值兼容?
    • @LucasPG 转换按索引降序应用。第一个将球体从原点平移,这意味着您需要将50 替换为半径。下一个转换是旋转以将节点移动到 x/y 窗格中的正确角度。可以使用360 * V_xy / (2 * PI*r) 计算角速度。最后一次变换沿 z 方向移动球体。最后 2 个值通过 2 帧的给定值之间的插值进行动画处理。鉴于提到的值,想出一个合适的时间跨度并在开始和结束时计算 vals 应该不难
    • 我已经计算了所有值。我将半径更改为 50,将 Vz 更改为 900d,将 Vxy 更改为 720。是正确的思维方式吗?
    猜你喜欢
    • 2018-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多