简体   繁体   English

拖动鼠标以在Java中旋转和缩放

[英]Drag mouse to rotate and scale in Java

I'm trying to create an Illustrator style selection box for geometric objects in java. 我正在尝试为Java中的几何对象创建Illustrator样式选择框。

选择框

When the object is selected a border is drawn and it's possible to drag the little rectangles to re-size the object. 选择对象后,将绘制边框,并且可以拖动小矩形以调整对象大小。 I'd also like to be able to rotate the box by dragging. 我还希望能够通过拖动来旋转框。

So far I can scale the box and I can rotate the box but I can't do the two together. 到目前为止,我可以缩放盒子,也可以旋转盒子,但是我不能将两者一起做。 Imagine the box is at an angle of 45 degrees. 想象一下,盒子是成45度角的。 When you drag the corner to enlarge the box in the x direction this will increase both the width and height of the box because of the angle. 当拖动角沿x方向放大框时,由于角度的关系,框的宽度和高度都会增加。

I can get it to work by using: 我可以使用以下方法使其工作:

    dx = dx*cos(theta) - dy*sin(theta);
    dy = dy*cos(theta) + dx*sin(theta);

But this only works when the pivot point is in the top left corner. 但这仅在枢轴点位于左上角时有效。 I want to be able to move the pivot around and then scale and rotate. 我希望能够绕枢轴移动,然后缩放和旋转。 This problem must have been solved lots of times before. 这个问题必须已经解决了很多次。 Is there a way I can use an affine transform to convert my mouse draw to the coordinate space of the rotated object? 有没有办法使用仿射变换将鼠标绘制转换为旋转对象的坐标空间? I'd prefer not to have to dig through the trigonometry! 我不想不必去研究三角学! Thanks in advance. 提前致谢。

You are in luck - Java2D provides an AffineTransform class that should do everything you are looking for. 幸运的是-Java2D提供了AffineTransform类,该类可以完成您要查找的所有内容。

It can handle rotations, scaling, shears, flips, translations etc. 它可以处理旋转,缩放,剪切,翻转,平移等。

There is a concatenate function that should enable you to combine multiple transforms (as moonwave99 points out you need to do them in the right order as the combination of affine transformations is not commutative) 有一个串联函数可以使您组合多个变换(因为moonwave99指出,您需要以正确的顺序进行变换,因为仿射变换的组合不是可交换的)

I pretty much worked the answer out myself although it's still not possible to move the pivot point around. 我几乎自己解决了这个问题,尽管仍然无法移动枢轴点。 In case it's helpful here's the full code for a working example using JavaFX 2.2. 如果有帮助,这里是使用JavaFX 2.2的工作示例的完整代码。 You can scale and rotate the box by dragging the corners around: 您可以通过拖动四角来缩放和旋转框:

public class SelectionBoxDemo extends Application {

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

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

    // Root is the base pane in which we put everything
    Pane root = new Pane ();

    SelectionBox sb = new SelectionBox ();

    sb.setSize(100, 100);

    root.getChildren().add(sb);

    // Create a new scene
    Scene scene = new Scene (root);

    stage.setScene(scene);

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

    stage.show();
}

public static class SelectionBox extends Region {

    private enum Position {
        TopLeft, Top, TopRight, Right, BottomRight, Bottom, BottomLeft, Left; 
    }

    // Create the corners
    private Rectangle tr, tl, br, bl;

    // Create selection lines
    final private Line top, right, bottom, left;

    // Size of corner boxes
    private double cornerSize = 10;

    // Create a new rotate transform
    private final Rotate rotate = new Rotate();
    {
        getTransforms().add(rotate);
        rotate.setPivotX(cornerSize);
        rotate.setPivotY(cornerSize);
    }

    // Circle which is dragged to rotate the box
    private final Circle rotateCircle;

    // Variables to store mouse x and y
    private double x, y;

    public SelectionBox () {

        // Create the circle which can be dragged to rotate the box
        rotateCircle = new Circle(5);
        rotateCircle.setFill(Color.PINK);
        rotateCircle.setStroke(Color.rgb(0,0,0, 0.75));

        // Make it draggable
        rotateCircle.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent event) {
                setMouse(event.getSceneX(), event.getSceneY());
            }
        });

        // When it's dragged rotate the box
        rotateCircle.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent event) {

                // Used to get the scene position of the corner of the box
                Transform localToScene = getLocalToSceneTransform();

                double x1 = getMouseX();
                double y1 = getMouseY();

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

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

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

                double angle = rotate.getAngle();

                angle += th2 - th1;

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

                setMouse(event.getSceneX(), event.getSceneY());
            }
        });

        // Build the corners
        tr = buildCorner (0,0, Position.TopRight);
        tl = buildCorner (0,0, Position.TopLeft);
        br = buildCorner (0,0, Position.BottomRight);
        bl = buildCorner (0,0, Position.BottomLeft);

        // Build the lines
        top = buildLine(0, 100, -100, 0);
        bottom = buildLine(0, 0, 0, 0);
        left = buildLine(0, 0, 0, 0);
        right = buildLine(0, 0, 0, 0);

        getChildren().addAll(top, bottom, left, right, tr, tl, br, bl, rotateCircle);

    }

    // Return the angle from 0 - 360 degrees
    public double clockAngle (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;
    }

    // Set the size of the selection box
    public void setSize (double width, double height) {
        tl.setX(0);
        tl.setY(0);

        tr.setX(width + cornerSize);
        tr.setY(0);

        bl.setX(0);
        bl.setY(height + cornerSize);

        br.setX(width + cornerSize);
        br.setY(height + cornerSize);

        setLine(top, cornerSize, cornerSize, width + cornerSize, cornerSize);
        setLine(bottom, cornerSize, height + cornerSize, width + cornerSize, height + cornerSize);
        setLine(right, width + cornerSize, cornerSize, width + cornerSize, height + cornerSize);
        setLine(left, cornerSize, cornerSize, cornerSize, height + cornerSize);

        top.setCursor(Cursor.V_RESIZE);
        bottom.setCursor(Cursor.V_RESIZE);
        left.setCursor(Cursor.H_RESIZE);
        right.setCursor(Cursor.H_RESIZE);

        tr.setCursor(Cursor.CROSSHAIR);
        tl.setCursor(Cursor.CROSSHAIR);
        br.setCursor(Cursor.CROSSHAIR);
        bl.setCursor(Cursor.CROSSHAIR);

        rotateCircle.setTranslateX(width + 2 * cornerSize + rotateCircle.getRadius());
        rotateCircle.setTranslateY(height + 2 * cornerSize + rotateCircle.getRadius());

    }

    // Set the start and end points of a line
    private void setLine (Line l, double x1, double y1, double x2, double y2) {
        l.setStartX(x1);
        l.setStartY(y1);
        l.setEndX(x2);
        l.setEndY(y2);
    }

    // Save mouse coordinates
    private void setMouse(double x, double y) {
        this.x = x;
        this.y = y;
    }

    private double getMouseX () {
        return x;
    }

    private double getMouseY () {
        return y;
    }

    // Selection box width
    public double w () {
        return Math.abs(bottom.getEndX() - bottom.getStartX());
    }

    // Selection box height
    public double h () {
        return Math.abs(right.getEndY() - right.getStartY());
    }

    // Build a corner of the rectangle
    private Rectangle buildCorner (double x, double y, final Position pos) {

        // Create the rectangle
        Rectangle r = new Rectangle();
        r.setX(x);
        r.setY(y);
        r.setWidth(cornerSize);
        r.setHeight(cornerSize);
        r.setStroke(Color.rgb(0,0,0,0.75));
        r.setFill(Color.rgb(0, 0, 0, 0.25));
        r.setStrokeWidth(1);

        r.setStrokeType(StrokeType.INSIDE);


        // Make it draggable
        r.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent event) {
                setMouse(event.getSceneX(), event.getSceneY());
            }
        });

        r.addEventHandler(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent event) {

                // Get the mouse deltas
                double dx = event.getSceneX() - getMouseX();
                double dy = event.getSceneY() - getMouseY();

                // Set save the current mouse value
                setMouse(event.getSceneX(), event.getSceneY());

                // Get the rotation angle in radians
                double tau = - Math.toRadians(rotate.getAngle());

                // Create variables for the sin and cosine
                double sinTau = Math.sin(tau);
                double cosTau = Math.cos(tau);

                // Perform a rotation on dx and dy to the object co-ordinate frame
                double dx_ = dx * cosTau - dy * sinTau;
                double dy_ = dy * cosTau + dx * sinTau;

                // Create a variable for the change in height of the box
                double dh = h();

                // Work out the new positions for the resize corners
                if(pos == Position.TopLeft) {
                    // Set the size based on the transformed dx and dy values
                    setSize(w() - dx_, h() - dy_);

                    // Move the shape 
                    setTranslateX(getTranslateX() + dx); 
                    setTranslateY(getTranslateY() + dy);
                }
                else if (pos == Position.TopRight) {

                    // This comes down to geometry - you need to know the 
                    // amount the height of the shape has increased
                    setSize(w() + dx_ , h() - dy_);

                    // Work out the delta height - that is then used to work out 
                    // the correct translations
                    dh = h() - dh;

                    setTranslateX (getTranslateX() - dh * sinTau);
                    setTranslateY (getTranslateY() - dh * cosTau);
                }
                else if (pos == Position.BottomRight) {
                    setSize(w() + dx_ , h() + dy_ );
                }
                else if (pos == Position.BottomLeft) {

                    setSize(w() - dx_, h() + dy_);

                    dh = h() - dh;

                    setTranslateX(getTranslateX() + dx - dh * sinTau );
                    setTranslateY(getTranslateY() + dy - dh * cosTau);
                }
            }
        });


        return r;
    }



    private Line buildLine (double x1, double y1, double x2, double y2) {
        Line l = new Line (x1, y1, x2, y2);

        l.setStroke(Color.rgb(0, 0, 0, 0.75));
        l.setStrokeWidth (0.5);

        return l;
    }


}

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM