同时旋转和平移在 javafx 中无法按预期工作

Simultaneous Rotate and Translate not working as expected in javafx

我从 CirclesLine 构建了以下 "system" .

我正在尝试在系统中执行两个转换。

  1. 拖动绿色圆时,绿色和蓝色圆都应围绕红色中心旋转,并沿拖动方向旋转。
  2. 当拖动 Red 中心时,整个系统应该相应地被拖动(像一个整体)。

我尝试实现它但遇到了问题。

两个转换单独完成时都工作正常。

当应用程序启动时,当您尝试拖动绿色圆圈时,整个系统完美地围绕红色圆圈旋转。
同样,在启动时,当拖动红色圆圈时,一切都被拖动得很好。 但是
当一个接一个地尝试这些拖动时,形状会相互远离!!

请提出可能导致此行为的原因。

也欢迎任何其他实现转换的方法,因为我还是新手到 JavaFx 中的动画,这显然不是解决问题的最佳代码。

以下是我的代码:

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.scene.shape.Line;
    import javafx.scene.transform.Rotate;
    import javafx.scene.transform.Translate;
    import javafx.stage.Stage;
    public class SpindleAndWheel extends Application
    {

    Circle circle;
    Circle oppositeCircle;
    Circle pivot;
    Line spindle;

    Rotate rotateCircle, rotateOppositeCircle;
    Translate translateCircle, translateOppositeCircle;
    Translate translateCircleBack, translateOppositeCircleBack;
    Translate dragTranslateCircle, dragTranslatePivot, dragTranslateOppositeCircle, dragTranslateSpindle;

    double dragInitX, dragInitY;
    double dragInitTheta;
    double theta1,theta2;
    double angleOfRotation;

    double dragPivotInitX,dragPivotInitY,systemDragOffsetX,systemDragOffsetY;
    double dragTranslateCircleBackupX,dragTranslatePivotBackupX, dragTranslateOppositeCircleBackupX, dragTranslateSpindleBackupX;
    double dragTranslateCircleBackupY,dragTranslatePivotBackupY, dragTranslateOppositeCircleBackupY, dragTranslateSpindleBackupY;

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        Stage stage = new Stage();


        stage.setMinHeight(500);
        stage.setMinWidth(500);

        Pane root = new Pane();

        circle = new Circle(150, 150, 30);
        oppositeCircle = new Circle(350, 350, 30);
        pivot = new Circle(250, 250, 5);
        spindle = new Line(150, 150, 350, 350);

    /**************** Initializing Shapes **********/
        circle.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.35));
        circle.setStroke(Color.GREEN);
        oppositeCircle.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.35));
        oppositeCircle.setStroke(Color.BLUE);
        pivot.setFill(Color.RED.deriveColor(1, 1, 1, 0.35));
        pivot.setStroke(Color.RED);
        spindle.setStrokeWidth(3);
        spindle.setStroke(Color.BLACK.deriveColor(1, 1, 1, 0.35));
    /****************************************************/

    /**************** Initializing Animations **********/
        rotateCircle = new Rotate(0, 0, 0);
        translateCircle = new Translate(0, 0);
        translateCircleBack = new Translate(0, 0);
        dragTranslateCircle = new Translate(0, 0);

        rotateOppositeCircle = new Rotate(0, 0, 0);
        translateOppositeCircle = new Translate(0, 0);
        translateOppositeCircleBack = new Translate(0, 0);
        dragTranslateOppositeCircle = new Translate(0, 0);


        dragTranslatePivot = new Translate(0, 0);
        dragTranslateSpindle = new Translate(0, 0);
    /****************************************************/

    /**************** Adding Animations **************/
        circle.getTransforms().addAll(translateCircle, rotateCircle, translateCircleBack, dragTranslateCircle);
        oppositeCircle.getTransforms().addAll(translateOppositeCircle, rotateOppositeCircle, translateOppositeCircleBack, dragTranslateOppositeCircle);
        pivot.getTransforms().addAll(dragTranslatePivot);
        spindle.getTransforms().addAll(dragTranslateSpindle);
    /****************************************************/
        Scene scene = new Scene(root);

        stage.setScene(scene);


        root.getChildren().addAll(circle,oppositeCircle,pivot,spindle);
        stage.show();



    /** Get angle of point of click with
     * point of rotation, before drag begins **/
        circle.setOnMousePressed(event -> {
            dragInitX = event.getSceneX();
            dragInitY = event.getSceneY();
            dragInitTheta = angleOfRotation + dragInitTheta;

            double initialSlope;
            double pointOfRotationX,pointOfRotationY;
            pointOfRotationY = pivot.getCenterY() + dragTranslatePivot.getY();
            pointOfRotationX = pivot.getCenterX() + dragTranslatePivot.getX();

            initialSlope = Math.atan((dragInitY - (pointOfRotationX)) / (dragInitX - (pointOfRotationY)));

            theta1 = Math.toDegrees(initialSlope);

            if (dragInitX < pointOfRotationX)
                theta1 = (360 + theta1) % 360;
            else
                theta1 = 180 + theta1;

        });
    /********************************************/

    /** Get angle of point of drags with
    * point of rotation, when the drag is on **/
       circle.setOnMouseDragged(event -> {
           double finalSlope;
           double pointOfRotationX,pointOfRotationY;
           pointOfRotationY = pivot.getCenterY() + dragTranslatePivot.getY();
           pointOfRotationX = pivot.getCenterX() + dragTranslatePivot.getX();

           finalSlope = Math.atan((event.getSceneY() - (pointOfRotationX)) / (event.getSceneX() - (pointOfRotationY)));
           theta2 = Math.toDegrees(finalSlope);


           if (event.getSceneX() < pointOfRotationX)
               theta2 = (360 + theta2) % 360;
           else
               theta2 = 180 + theta2;

           angleOfRotation = theta2 - theta1;

           translateCircle.setX(pointOfRotationX);
           translateCircle.setY(pointOfRotationY);
           rotateCircle.setAngle(angleOfRotation + dragInitTheta);
           translateCircleBack.setX(-pointOfRotationX);
           translateCircleBack.setY(-pointOfRotationY);

           translateOppositeCircle.setX(pointOfRotationX);
           translateOppositeCircle.setY(pointOfRotationY);
           rotateOppositeCircle.setAngle(angleOfRotation + dragInitTheta);
           translateOppositeCircleBack.setX(-pointOfRotationX);
           translateOppositeCircleBack.setY(-pointOfRotationY);

           spindle.setRotate(angleOfRotation + dragInitTheta);
       });
    /********************************************/



    /** Relocate the whole system when the point of
     * rotation is dragged **/
        pivot.setOnMousePressed(event -> {
            dragPivotInitX = event.getSceneX();
            dragPivotInitY = event.getSceneY();

            dragTranslateCircleBackupX = dragTranslateCircle.getX();
            dragTranslateCircleBackupY = dragTranslateCircle.getY();
            dragTranslatePivotBackupX = dragTranslatePivot.getX();
            dragTranslatePivotBackupY = dragTranslatePivot.getY();
            dragTranslateOppositeCircleBackupX = dragTranslateOppositeCircle.getX();
            dragTranslateOppositeCircleBackupY = dragTranslateOppositeCircle.getY();
            dragTranslateSpindleBackupX = dragTranslateSpindle.getX();
            dragTranslateSpindleBackupY = dragTranslateSpindle.getY();
        });

        pivot.setOnMouseDragged(event -> {
            systemDragOffsetX = event.getSceneX() - dragPivotInitX;
            systemDragOffsetY = event.getSceneY() - dragPivotInitY;

            dragTranslateCircle.setX(dragTranslateCircleBackupX + systemDragOffsetX);
            dragTranslateCircle.setY(dragTranslateCircleBackupY + systemDragOffsetY);
            dragTranslatePivot.setX(dragTranslatePivotBackupX+ systemDragOffsetX);
            dragTranslatePivot.setY(dragTranslatePivotBackupY+ systemDragOffsetY);
            dragTranslateOppositeCircle.setX(dragTranslateOppositeCircleBackupX+ systemDragOffsetX);
            dragTranslateOppositeCircle.setY(dragTranslateOppositeCircleBackupY+ systemDragOffsetY);
            dragTranslateSpindle.setX(dragTranslateSpindleBackupX+ systemDragOffsetX);
            dragTranslateSpindle.setY(dragTranslateSpindleBackupY + systemDragOffsetY);

        });
    /********************************************/

    }

    public static void main(String[] args)
    {
        launch(args);
    }


}

不错的方法,但正如您已经发现的那样,它有一个主要缺点:应用一个转换后,另一个转换效果不佳。原因在于您定义转换的方式。此外,分配如此多的转换导致系统复杂,难以维护。

我的建议要简单得多:将所有形状包裹在一个 Group 中,并且只变换这组,在拖动轴时平移它,或者在轴上旋转它,当你拖动圆圈。注意这里可以用setPivotX()setPivotY()来表示实际的旋转点

如您所见,您只需要两个转换,更重要的是,以任何顺序应用其中任何一个都不会产生任何副作用。

编辑

正如 OP 正确指出的那样,旋转计算存在错误。为了完整起见,我编辑了我的答案以显示正确的代码。

private Circle circle;
private Circle oppositeCircle;
private Circle pivot;
private Line spindle;

private Group group;

private Rotate rotateGroup;
double dragInitTheta;
double theta1,theta2;
double angleOfRotation;

private Translate translateGroup;
private double dragTranslateGroupBackupX, dragTranslateGroupBackupY;  
private double dragPivotInitX,dragPivotInitY,systemDragOffsetX,systemDragOffsetY;

@Override
public void start(Stage stage){
    circle = new Circle(150, 150, 30);
    oppositeCircle = new Circle(350, 350, 30);
    pivot = new Circle(250, 250, 5);
    spindle = new Line(150, 150, 350, 350);

    group = new Group(circle,oppositeCircle,pivot,spindle);

/**************** Initializing Shapes **********/
    circle.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.35));
    circle.setStroke(Color.GREEN);
    oppositeCircle.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.35));
    oppositeCircle.setStroke(Color.BLUE);
    pivot.setFill(Color.RED.deriveColor(1, 1, 1, 0.35));
    pivot.setStroke(Color.RED);
    spindle.setStrokeWidth(3);
    spindle.setStroke(Color.BLACK.deriveColor(1, 1, 1, 0.35));
/****************************************************/

/**************** Initializing Transforms **********/
    rotateGroup = new Rotate(0, 0, 0);
    translateGroup = new Translate(0, 0);
/****************************************************/

/**************** Adding Transforms **************/
    group.getTransforms().addAll(translateGroup,rotateGroup);
/****************************************************/
    Pane root = new Pane(group);
    Scene scene = new Scene(root,500,500);

    stage.setScene(scene);
    stage.show();

/** Get angle of point of click with
 * point of rotation, before drag begins **/
    circle.setOnMousePressed(event -> {
        dragInitTheta = angleOfRotation + dragInitTheta;
        double pointOfRotationX = pivot.getCenterX() + translateGroup.getX();
        double pointOfRotationY = pivot.getCenterY() + translateGroup.getY();


        double initialSlope = Math.atan((event.getSceneY() - pointOfRotationY) / (event.getSceneX() - pointOfRotationX));
        theta1 = Math.toDegrees(initialSlope);

        if (event.getSceneX() < pointOfRotationX)
            theta1 = (360 + theta1) % 360;
        else
            theta1 = 180 + theta1;

    });
/********************************************/

/** Get angle of point of drags with
* point of rotation, when the drag is on **/
   circle.setOnMouseDragged(event -> {
        double pointOfRotationX = pivot.getCenterX() + translateGroup.getX();
        double pointOfRotationY = pivot.getCenterY() + translateGroup.getY();
        double finalSlope = Math.atan((event.getSceneY() - pointOfRotationY) / (event.getSceneX() - pointOfRotationX));
        theta2 = Math.toDegrees(finalSlope);

        if (event.getSceneX() < pointOfRotationX)
            theta2 = (360 + theta2) % 360;
        else
            theta2 = 180 + theta2;

        angleOfRotation = theta2 - theta1;
        rotateGroup.setPivotX(pivot.getCenterX());
        rotateGroup.setPivotY(pivot.getCenterY());
        rotateGroup.setAngle(angleOfRotation + dragInitTheta);
   });
/********************************************/

/** Relocate the whole system when the point of
 * rotation is dragged **/
    pivot.setOnMousePressed(event -> {
        dragPivotInitX = event.getSceneX();
        dragPivotInitY = event.getSceneY();

        dragTranslateGroupBackupX = translateGroup.getX();
        dragTranslateGroupBackupY = translateGroup.getY();
    });

    pivot.setOnMouseDragged(event -> {
        systemDragOffsetX = event.getSceneX() - dragPivotInitX;
        systemDragOffsetY = event.getSceneY() - dragPivotInitY;

        translateGroup.setX(dragTranslateGroupBackupX + systemDragOffsetX);
        translateGroup.setY(dragTranslateGroupBackupY + systemDragOffsetY);

    });
/********************************************/

}

José Pereda很好的答案!! 更好的方法。
学习了 许多 个新事物。
只需要做一个简单的修正。
在计算阻力点的斜率时,还需要考虑由于(translateGroup.getY(),translateGroup.getX())[=25=引起的偏移量] 在 (pivot.getCenterY(),pivot.getCenterX()).

以下是经过上述更正的 José 代码。

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javafx.scene.Group;

public class SpindleAndWheelGroup extends Application
{
    private Circle circle;
    private Circle oppositeCircle;
    private Circle pivot;
    private Line spindle;

    private Group group;

    private Rotate rotateGroup;
    double dragInitTheta;
    double theta1, theta2;
    double angleOfRotation;

    private Translate translateGroup;
    private double dragTranslateGroupBackupX, dragTranslateGroupBackupY;
    private double dragPivotInitX, dragPivotInitY, systemDragOffsetX, systemDragOffsetY;

    double pointOfRotationX,pointOfRotationY;

    @Override
    public void start(Stage stage)
    {
        circle = new Circle(150, 150, 30);
        oppositeCircle = new Circle(350, 350, 30);
        pivot = new Circle(250, 250, 5);
        spindle = new Line(150, 150, 350, 350);

        group = new Group(circle, oppositeCircle, pivot, spindle);

/**************** Initializing Shapes **********/
        circle.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.35));
        circle.setStroke(Color.GREEN);
        oppositeCircle.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.35));
        oppositeCircle.setStroke(Color.BLUE);
        pivot.setFill(Color.RED.deriveColor(1, 1, 1, 0.35));
        pivot.setStroke(Color.RED);
        spindle.setStrokeWidth(3);
        spindle.setStroke(Color.BLACK.deriveColor(1, 1, 1, 0.35));
/****************************************************/

/**************** Initializing Transforms **********/
        rotateGroup = new Rotate(0, 0, 0);
        translateGroup = new Translate(0, 0);
/****************************************************/

/**************** Adding Transforms **************/
        group.getTransforms().addAll(translateGroup, rotateGroup);
/****************************************************/
        Pane root = new Pane(group);
        Scene scene = new Scene(root, 800, 700);

        stage.setScene(scene);
        stage.show();

/** Get angle of point of click with
 * point of rotation, before drag begins **/
        circle.setOnMousePressed(event -> {
            dragInitTheta = angleOfRotation + dragInitTheta;
            pointOfRotationY = pivot.getCenterY() + translateGroup.getY();
            pointOfRotationX = pivot.getCenterX() + translateGroup.getX();

            double initialSlope = Math.atan((event.getSceneY() - pointOfRotationY) / (event.getSceneX() - pointOfRotationX));
            theta1 = Math.toDegrees(initialSlope);

            if (event.getSceneX() < pointOfRotationX)
                theta1 = (360 + theta1) % 360;
            else
                theta1 = 180 + theta1;

        });
/********************************************/

/** Get angle of point of drags with
 * point of rotation, when the drag is on **/
        circle.setOnMouseDragged(event -> {
            pointOfRotationY = pivot.getCenterY() + translateGroup.getY();
            pointOfRotationX = pivot.getCenterX() + translateGroup.getX();

            double finalSlope = Math.atan((event.getSceneY() - pointOfRotationY) / (event.getSceneX() - pointOfRotationX));
            theta2 = Math.toDegrees(finalSlope);

            if (event.getSceneX() < pointOfRotationX)
                theta2 = (360 + theta2) % 360;
            else
                theta2 = 180 + theta2;

            angleOfRotation = theta2 - theta1;
            rotateGroup.setPivotX(pivot.getCenterX());
            rotateGroup.setPivotY(pivot.getCenterY());
            rotateGroup.setAngle(angleOfRotation + dragInitTheta);
        });
/********************************************/

/** Relocate the whole system when the point of
 * rotation is dragged **/
        pivot.setOnMousePressed(event -> {
            dragPivotInitX = event.getSceneX();
            dragPivotInitY = event.getSceneY();

            dragTranslateGroupBackupX = translateGroup.getX();
            dragTranslateGroupBackupY = translateGroup.getY();
        });

        pivot.setOnMouseDragged(event -> {
            systemDragOffsetX = event.getSceneX() - dragPivotInitX;
            systemDragOffsetY = event.getSceneY() - dragPivotInitY;

            translateGroup.setX(dragTranslateGroupBackupX + systemDragOffsetX);
            translateGroup.setY(dragTranslateGroupBackupY + systemDragOffsetY);

        });
/********************************************/

    }
}