JavaFX8三维动画

JavaFX8三维动画,java,animation,javafx,3d,Java,Animation,Javafx,3d,我正在做一个小JavaFX8项目。我需要一些关于如何正确制作动画的建议。关于这个项目。这是一个使带电粒子在磁场中流动的程序。所有需要的值都取自GUI,用户将它们放入文本字段中。单击按钮后,我们将被转移到3D场景,其中我的点显示为球体,所有值都已设置。字段行被打印在它的目录中 问题是如何制作propper动画。我试图使用我的球体X Y Z坐标,但找不到任何方法来设置这些坐标。Z平面中的运动应为线性,且速度相同。而XY平面中的运动应该是圆形的。我可以用路径转换来完成吗 我的愿景是创建勾号动画,通过路

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

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


我的愿景是创建勾号动画,通过路径绘制球体。在进一步勾选的地方,下一个球体将使用平移向量计算的新坐标绘制。

这是否可能使用路径转换?不,在javafx中,路径是二维的,但您需要三维移动

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

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

您可以使用多个变换,并使用
时间轴
动画设置这些变换的动画,以实现这种运动:

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();
}
您的运动是螺旋运动,因此以下变换组合将适当地移动
球体

  • 按半径平移(z分量0)
  • 旋转到适当的角度
  • 沿z方向平移
  • 注意:变换的应用顺序与它们在
    变换列表中出现的顺序相反


    或者,您可以编写可与圆柱体坐标参数一起使用的辅助对象,并编写相应的
    x
    y
    z
    值:

    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;
        }
    
    }
    

    非常感谢你!你会在这里回答我进一步的问题吗?@LucasPG如果是关于这个答案的问题,就写一条评论。如果是新问题:我通常每天回答几个问题。如果你写了一个问题,它写得很好,很有趣,没有足够高质量的答案,我知道答案,我可能会发布它。。。不过,也有其他相当有能力的用户可以回答您的问题…您好。在我的应用程序中,用户输入所有neded值。我已经计算了圆周运动的半径,还有XY平面上的速度和通过Z轴的速度。如何将所有这3个值放入animateSphere函数,使移动与用户值兼容?@LucasPG变换以递减的索引顺序应用。第一个将球体从原点移开,这意味着您需要将
    50
    替换为半径。下一个变换是旋转,以将节点移动到x/y窗格中的正确角度。可以使用
    360*V_xy/(2*PI*r)
    计算角速度。最后一个变换沿z方向移动球体。最后两个值通过在给定值之间插值为两帧设置动画。考虑到上述值,找出合适的时间跨度并计算起始和结束时的VAL应该不会太难。我已经计算了所有值。我把半径改为50,把Vz改为900d,把Vxy改为720。这是正确的思维方式吗?
    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();
    }