调整 javafx 大小时保持矩形旋转轴位于中心

问题描述 投票:0回答:2

我最近开始学习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
2个回答
0
投票

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

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

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

无论如何,更简单的方法是使用

Scale
转型。有了这个,您可以指定“枢轴”点 发生规模。

如本 so 答案中所解释和演示。
通过使用 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); } }
    

-1
投票
“更改旋转的枢轴时,它还会影响更改之前执行的旋转。这就是您看到的“跳跃”的原因。”

谢谢。如何避免这种“跳跃”?

© www.soinside.com 2019 - 2024. All rights reserved.