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

在Java中拖动鼠标进行旋转和缩放,java,rotation,geometry,scale,affinetransform,Java,Rotation,Geometry,Scale,Affinetransform,我正在尝试为java中的几何对象创建Illustrator样式选择框 选择对象后,将绘制边框,可以拖动小矩形来重新调整对象的大小。我还希望能够通过拖动来旋转长方体 到目前为止,我可以缩放长方体,也可以旋转长方体,但我不能同时做这两件事。假设长方体呈45度角。当拖动角点以在x方向放大长方体时,由于角度的原因,这将增加长方体的宽度和高度 我可以使用以下方法使其工作: dx = dx*cos(theta) - dy*sin(theta); dy = dy*cos(theta) + d

我正在尝试为java中的几何对象创建Illustrator样式选择框

选择对象后,将绘制边框,可以拖动小矩形来重新调整对象的大小。我还希望能够通过拖动来旋转长方体

到目前为止,我可以缩放长方体,也可以旋转长方体,但我不能同时做这两件事。假设长方体呈45度角。当拖动角点以在x方向放大长方体时,由于角度的原因,这将增加长方体的宽度和高度

我可以使用以下方法使其工作:

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

但这仅在轴心点位于左上角时有效。我希望能够移动轴,然后缩放和旋转。这个问题以前一定解决过很多次了。是否有一种方法可以使用仿射变换将鼠标绘制转换为旋转对象的坐标空间?我宁愿不去钻研三角学!提前感谢。

你很幸运-Java2D提供了一个类,它可以完成你想要的一切

它可以处理旋转、缩放、剪切、翻转、平移等操作


有一个函数可以让你组合多个变换(正如moonwave99所指出的那样,你需要按照正确的顺序进行变换,因为仿射变换的组合不是可交换的)

我自己几乎已经找到了答案,尽管仍然无法移动轴心点。如果有帮助,下面是使用JavaFX2.2的工作示例的完整代码。可以通过拖动以下角点来缩放和旋转长方体:

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


}
公共类SelectionBoxDemo扩展应用程序{
公共静态void main(字符串[]args){
发射(args);
}
@凌驾
public void start(阶段arg0)引发异常{
阶段=新阶段();
//Root是我们放置所有内容的基本窗格
窗格根=新窗格();
SelectionBox sb=新的SelectionBox();
某人设定尺寸(100100);
root.getChildren().add(sb);
//创建一个新场景
场景=新场景(根);
舞台场景;
舞台设置最小高度(500);
舞台设置最小宽度(500);
stage.show();
}
公共静态类选择框扩展了区域{
私有枚举位置{
左上、右上、右上、右下、右下、左下、左下;
}
//创建角点
私有矩形tr、tl、br、bl;
//创建选择线
最终专用线顶部、右侧、底部、左侧;
//角盒尺寸
私人双转角尺寸=10;
//创建新的旋转变换
私有最终旋转=新旋转();
{
getTransforms().add(旋转);
旋转.setPivotX(角尺寸);
旋转.setPivotY(角尺寸);
}
//拖动以旋转长方体的圆
专用最终圆旋转圆;
//用于存储鼠标x和y的变量
私人双x,y;
公共选择框(){
//创建可以拖动以旋转长方体的圆
旋转圆=新的圆(5);
旋转圆。毛填充(颜色。粉红色);
旋转圆设定行程(颜色rgb(0,0,0,0.75));
//让它拖拉
rotateCycle.addEventHandler(按下MouseEvent.MOUSE_,新建EventHandler()){
@重写公共无效句柄(MouseeEvent事件){
setMouse(event.getSceneX(),event.getSceneY());
}
});
//拖动后,旋转长方体
rotateCircle.addEventHandler(MouseEvent.MOUSE_拖动,新事件处理程序(){
@重写公共无效句柄(MouseeEvent事件){
//用于获取长方体角的场景位置
Transform localToScene=GetLocalToScenetTransform();
double x1=getMouseX();
双y1=getMouseY();
double x2=event.getSceneX();
double y2=event.getSceneY();
double px=rotate.getPivotX()+localToScene.getTx();
double py=rotate.getPivotY()+localToScene.getTy();
//算出旋转的角度
双th1=时钟角(x1,y1,px,py);
双th2=时钟角(x2,y2,px,py);
双角度=旋转.getAngle();
角度+=th2-th1;
//旋转矩形
旋转。设置角度(角度);
setMouse(event.getSceneX(),event.getSceneY());
}
});
//建造角落
tr=建筑角(0,0,位置右上角);
tl=建筑角(0,0,位置左上角);
br=建筑角(0,0,位置。右下角);
bl=建筑角(0,0,位置。左下角);
//建造线路
顶部=建筑线(0,100,-100,0);
底部=构建线(0,0,0,0);
左=构建线(0,0,0,0);
右=构建线(0,0,0,0);
getChildren().addAll(顶部、底部、左侧、右侧、tr、tl、br、bl、旋转圆);
}
//返回0-360度的角度
公共双时钟角(双x、双y、双px、双py){
双dx=x-px;
双dy=y-py;
双角度=Math.abs(Math.toDegrees(Math.atan2(dy,dx));
if(dy<0){
角度=360度-角度;
}
返回角;
}
//设置选择框的大小
公共空隙设置尺寸(双倍宽度,双倍高度){
tl.setX(0);
tl.setY(0);
tr.setX(宽度+拐角尺寸);
tr.setY(0);
bl.setX(0);
bl.setY(高度+转角尺寸);
br.setX(宽度+拐角尺寸);
br.setY(高度+转角尺寸);
设置线(顶部、拐角尺寸、拐角尺寸、宽度+拐角尺寸、拐角尺寸);
设置线(底部、拐角尺寸、高度+拐角尺寸、宽度+拐角尺寸、高度+拐角尺寸);
设置线(右侧、宽度+拐角尺寸、拐角尺寸、宽度+拐角尺寸、高度+拐角尺寸);