【问题标题】:keep rectangle rotation pivot on center when resized javafx调整javafx大小时保持矩形旋转枢轴在中心
【发布时间】:2021-12-27 00:09:18
【问题描述】:

我最近开始学习 javafx。在几篇文章的帮助下,我创建了一个可拖动、可调整大小和可旋转的矩形。我的计划是用它来调整自定义节点的大小。我希望旋转轴保持在矩形中心,但如果调整大小,中心会发生变化,当我设置新中心时,矩形会产生凹凸。为什么会发生这种情况以及我如何解决它?如果犯了任何错误,我很抱歉英语不是我的母语。提前致谢


import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.stage.Stage;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.transform.Rotate;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        final Delta dragDelta = new Delta();
        Wrapper<Point2D> mouseLocation = new Wrapper<>();
        
        Pane root = new Pane();
        
        Rectangle rect = new Rectangle(100,100);
        final Rotate rotate = new Rotate();
        {   
            //set pivot on rectangle center
            rotate.setPivotX((rect.getX() + rect.getWidth())/2);
            rotate.setPivotY((rect.getY() + rect.getHeight())/2);
        }
        
        rect.setStyle(
                    "-fx-stroke: blue; " +
                    "-fx-stroke-width: 2px; " +
                    "-fx-stroke-dash-array: 12 2 4 2; " +
                    "-fx-stroke-dash-offset: 6; " +
                    "-fx-stroke-line-cap: butt; " +
                    "-fx-fill: rgba(255, 255, 255, .0);"
        );
        
        Group group = new Group();
        
        Circle rotateCircle = new Circle(7);
        Circle topLeft = new Circle(7);

        topLeft.setOnMousePressed(e->{
            mouseLocation.value = new Point2D(e.getSceneX(), e.getSceneY());
        });
        
        topLeft.setOnMouseDragged(e->{
            // Get the mouse deltas
            double dx = e.getSceneX() - mouseLocation.value.getX();
            double dy = e.getSceneY() - mouseLocation.value.getY();

            dragDelta.x = e.getSceneX();
            dragDelta.x = e.getSceneY();
            
            // Get the angle in radians
            double tau = - Math.toRadians(rotate.getAngle());

            double sinTau = Math.sin(tau);
            double cosTau = Math.cos(tau);
            
            // Perform a rotation on dx and dy to the object coordinate
            double dx_ = dx * cosTau - dy * sinTau;
            double dy_ = dy * cosTau + dx * sinTau;
           
            rect.setWidth(w(rect) - dx_); 
            rect.setHeight(h(rect) - dy_);
            
            // Set save the current mouse value
            mouseLocation.value = new Point2D(e.getSceneX(), e.getSceneY());
                    
            // Move the control 
            group.setTranslateX(group.getTranslateX() + dx); 
            group.setTranslateY(group.getTranslateY() + dy);
        });

        topLeft.setOnMouseReleased(e->{
            //This is the problem
            rotate.setPivotX((rect.getX() + rect.getWidth())/2);
            rotate.setPivotY((rect.getY() + rect.getHeight())/2);
        });

        group.getTransforms().add(rotate);
        
        rotateCircle.centerXProperty().bind(rect.xProperty().add(rect.widthProperty()).divide(2)); 
        rotateCircle.centerYProperty().bind(rect.yProperty().subtract(25d));
        
        topLeft.setCenterX(rect.getX());
        topLeft.setCenterY(rect.getY());
        
        group.getChildren().addAll(rect, rotateCircle, topLeft);
        
        group.setLayoutX(100);
        group.setLayoutY(100);
        
        root.getChildren().add(group);
        
        Util.enableDrag(rect, rotate);
        Util.makeRotable(rotateCircle, rotate);
        
        Scene scene = new Scene(root,400,400);
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();

    }
    
    public double w(Rectangle rect) {
        return Math.abs(rect.getWidth() - rect.getX());
    }
    
    public double h(Rectangle rect) {
        return Math.abs(rect.getHeight() - rect.getY());
    }
    
    // Return the angle from 0 to 360 
     public static double deltaAngle (double x, double y, double px, double py) {
        double dx = x - px;
        double dy = y - py;

        double angle = Math.abs(Math.toDegrees(Math.atan2(dy, dx)));

        if(dy < 0) {
             angle = 360 - angle;
         }

         return angle;
      }
     
    static class Util {
        static Wrapper<Point2D> mouseLocation = new Wrapper<>();
        
        // make a targetNode movable by dragging it around with the mouse.
        static void enableDrag(Rectangle rectangle, Rotate rotate) {
            final var dragDelta = new Delta();
            Parent parent = rectangle.getParent();
            
            rectangle.setOnMousePressed(mouseEvent -> {
               
                mouseLocation.value = new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY());

                // record a delta distance for the drag and drop operation.
                dragDelta.x = mouseEvent.getX() - mouseEvent.getX();
                dragDelta.y = mouseEvent.getY() - mouseEvent.getY();
                
                parent.getScene().setCursor(Cursor.MOVE);
                
            });
            
            rectangle.setOnMouseReleased(mouseEvent -> {
                mouseLocation.value = null ;
                
                parent.getScene().setCursor(Cursor.HAND);
              
                parent.relocate(parent.getLayoutX() + parent.getTranslateX(),
                        parent.getLayoutY() + parent.getTranslateY());
                parent.setTranslateX(0);
                parent.setTranslateY(0); 
                
                rotate.setPivotX((rectangle.getX() + rectangle.getWidth())/2);
                rotate.setPivotY((rectangle.getY() + rectangle.getHeight())/2);
                
            });
            
            rectangle.setOnMouseDragged(mouseEvent -> {
                // Get the mouse deltas
                double deltaX = mouseEvent.getSceneX() - mouseLocation.value.getX();
                double deltaY = mouseEvent.getSceneY() - mouseLocation.value.getY();

                parent.setTranslateX(parent.getTranslateX() + deltaX); 
                parent.setTranslateY(parent.getTranslateY() + deltaY);

                mouseLocation.value = new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY());
                
            });
            
        }

        // make a targetNode movable by dragging it around with the mouse.
        static void makeRotable(Circle circle, Rotate rotate) {
            final var dragDelta = new Delta();

         // Make it draggable
            circle.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
                        dragDelta.x = event.getSceneX();
                        dragDelta.x = event.getSceneY();
                    });

                    // When it's dragged rotate the box
            circle.addEventHandler(MouseEvent.MOUSE_DRAGGED, event -> {

                        var localToScene = circle.getParent().getLocalToSceneTransform();

                        double x1 = dragDelta.x;
                        double y1 = dragDelta.y;

                        var x2 = event.getSceneX();
                        var y2 = event.getSceneY();

                        var px = rotate.getPivotX() + localToScene.getTx();
                        var py = rotate.getPivotY() + localToScene.getTy();

                        // Work out the angle rotated
                        double th1 = deltaAngle(x1, y1, px, py);
                        double th2 = deltaAngle(x2, y2, px, py);

                        var angle = rotate.getAngle();

                        angle += th2 - th1;

                        // Rotate the rectangle
                        rotate.setAngle(angle);

                        dragDelta.x = event.getSceneX();
                        dragDelta.y = event.getSceneY();
                    });

                }

       
        }
    
    // records relative x and y co-ordinates.
    private static class Delta {
         double x, y;
    }
        
    static class Wrapper<T> {
       T value ; 
    } 
      

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

【问题讨论】:

    标签: javafx rotation pivot resizable


    【解决方案1】:

    您正在通过设置新的宽度和高度来缩放矩形:

    rect.setWidth(w(rect) - dx_);
    rect.setHeight(h(rect) - dy_);
    

    这会保持矩形的原点,但会改变其中心。
    相反,请考虑围绕其中心缩放矩形:

    无论如何,更简单的方法是使用 Scale 转型。有了这个,你可以指定关于“枢轴”点 发生的规模。

    正如so answer 中所解释和演示的那样。
    通过使用Scale.setX 和 Scale.setY,您可以应用不同的宽度和高度比例。

    编辑:
    在更改Rotate 的轴心时,它还会影响在更改之前 执行的旋转。这就是“跳”的原因 你看。
    为了演示它,运行以下 mre,其中一个按钮执行旋转,另一个仅更改枢轴:

    import javafx.application.*;
    import javafx.scene.*;
    import javafx.scene.control.*;
    import javafx.scene.layout.*;
    import javafx.scene.shape.*;
    import javafx.scene.transform.*;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
        @Override
        public void start(Stage primaryStage) {
    
            Rectangle rect = new Rectangle(100,100);
            rect.setStyle(
                "-fx-stroke: blue; " +
                "-fx-fill: rgba(255, 255, 255, .0);"
            );
    
            rect.setLayoutX(100);
            rect.setLayoutY(100);
    
            Rotate rotate = new Rotate();
            rotate.setPivotX((rect.getX() + rect.getWidth())/2);
            rotate.setPivotY((rect.getY() + rect.getHeight())/2);
            rect.getTransforms().add(rotate);
    
            Button rotateBtn = new Button("Rotate");
    
            Button movePivotBtn = new Button("Move Rotate Pivot");
            movePivotBtn.setDisable(true);
            movePivotBtn.setLayoutX(60);
    
            rotateBtn.setOnAction(e->{
                rotate.setAngle(45);
                rotateBtn.setDisable(true);
                movePivotBtn.setDisable(false);
            });
    
            movePivotBtn.setOnAction(e->{
                rotate.setPivotX(rotate.getPivotX()+15);
                movePivotBtn.setDisable(true);
            });
    
            Pane root = new Pane(rotateBtn,  movePivotBtn, rect);
    
            Scene scene = new Scene(root,400,400);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

    • 感谢您的回答。我想要的是矩形被调整大小(宽度,高度)而不是缩放。解决方案可能是更改矩形 X,Y 的原点,但我不知道如何获取新值,因为这是一个旋转的矩形。有什么想法吗?
    • 请参考我编辑的答案。
    • 在那个例子中很容易获得新的比例增量。在我的情况下,如何在左上角手柄被拖动时获得。我的目标是通过一个角调整矩形的大小,同时保持对角在同一位置。
    • 我的目标是调整矩形的一个角,同时保持对角在同一位置。我在问题中没有看到这条基本信息。
    • 对不起。如果您运行发布的代码,您可以看到该部分已完成,当您拖动左上角的锚点时,对角保持在同一位置。问题是,当我将旋转枢轴设置为新的矩形中心时,该矩形会产生“凹凸”,直到您说发生这种情况,因为原点并没有改变形状宽度/高度,我才知道为什么。所以解决方案是在我的情况下将新的 X、Y 值设置为我可以在未旋转的矩形中做的事情。因为这个矩形可以旋转并且三角函数对我来说不清楚,所以我还没有修复。也许必须做一个新问题。
    猜你喜欢
    • 1970-01-01
    • 2018-06-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 1970-01-01
    • 2017-03-25
    • 2015-02-11
    相关资源
    最近更新 更多