Ahmed Elshorbagy
Ahmed Elshorbagy

Reputation: 611

resizing javafx shapes positions without scaling them

i wan't to create effect similar to that of photoshop when you press ctrl+T that produces frame around the object and resize but i want to just change the position of the contained object not scaling them what i created so far does the job but the movement is exagerrated and i can't figure out what is wrong with my codehere i am just trying with the northwest handler thanks in advance here is images illustrating exactly what i want the first image is original group with blue circles are controllers enter image description here the second is the group after resizing the group by dragging the north west but the components gets scaled also enter image description here the third one is exactly what i want to achieve only change positions without changing the scale enter image description here

//this creates the bounding box

public static void cr_resizer(Group root,double x,double y,double X,double Y) {
    Rectangle major=new Rectangle(x-10,y-10,X-x+20,Y-y+20);
    major.setFill(Color.TRANSPARENT);
    major.setId("major");
    major.setStrokeWidth(3);
    major.setStroke(Color.BLUE);
    major.setDisable(true);
    Circle NW=new Circle(x-10,y-10,3);
    Circle N=new Circle(((x+X)/2),y-10,3);
    Circle NE=new Circle(X+10,y-10,3);
    Circle W=new Circle(x-10,(Y+y)/2,3);
    Circle E=new Circle(X+10,(Y+y)/2,3);
    Circle SE=new Circle(X+10,Y+10,3);
    Circle S=new Circle(((x+X)/2),Y+10,3);
    Circle SW=new Circle(x-10,Y+10,3);
    resize_move( root,NW,SE);
    root.getChildren().addAll(major,NW,N,NE,SE,S,SW,E,W);

}
//this function calculates the calculate the distance of the mover point from the opposing point 
// to caclulate the scaling factor theen apply it to componnents of the group
public static void resize_move(Group root,Circle mover,Circle op) {
    double[]start={mover.getCenterX(),mover.getCenterY()};
    double[]end={op.getCenterX(),op.getCenterY()};
    double[]strt_length=dis2_vec_d(start, end);
    mover.setOnMouseClicked(ev->{
        difx=ev.getX();
        dify=ev.getY();
    });
    mover.setOnMouseDragged(ev->{
        double diffx=ev.getX()-difx;
        double diffy=ev.getY()-dify;
        System.out.println(diffx);
        System.out.println(diffy);
        difx=ev.getX();
        dify=ev.getY();
        mover.setCenterX(ev.getX());
        mover.setCenterY(ev.getY());
        double[] newp= {mover.getCenterX(),mover.getCenterY()};
        double[]end_length=dis2_vec_d(newp, end);
        double[]factor = {end_length[0]/strt_length[0],end_length[1]/strt_length[1]};
        for (Node nod:root.getChildren()) {
            if (nod instanceof Circle) {
                if (nod != mover) {
                    move_circ((Circle) nod,op,factor);
                }

            }
        }
    });

}
//this function relocate the position of the circle according to the position of the custom axis point
public static void move_circ(Circle cir,Circle op,double[]factor) {
double[] axis= {op.getCenterX(),op.getCenterY()};
double[] strt_point= {cir.getCenterX(),cir.getCenterY()};
double[] nstrt_point= new_cord(axis,strt_point,factor);
cir.setCenterX(nstrt_point[0]);
cir.setCenterY(nstrt_point[1]);

}
//this function scale the position of the point in relation to another point
public static double[] new_cord(double[]axis,double[]point,double[]factor) {
double[]strt_length=dis2_vec_d(point,axis);
double[]new_length= {strt_length[0]*factor[0],strt_length[1]*factor[1]};
double[]new_point= {new_length[0]+axis[0],new_length[1]+axis[1]};
return new_point;
}
//this function calculates the distance
public static double[] dis2_vec_d(double[]p1,double[]p2) {
double[] vec = new double[2];
vec[0]=p1[0]-p2[0];
vec[1]=p1[1]-p2[1];
return vec;
} 

Upvotes: 0

Views: 355

Answers (1)

Jose Martinez
Jose Martinez

Reputation: 11992

It looks like you just want to make your Node draggable. I created a class that does this, pasted below.

You might have to replace a few instances of a class called GameScreen in my code below.

public class Draggable {

    ////////////////////////////////////////////////////////////////////////////
    //variables
    AtomicBoolean myPressed = new AtomicBoolean(false);
    Point2D myInitialTranslates;
    Point2D dragInitialTranslates;
    EventHandler<? super MouseEvent> onMouseReleased;
    EventHandler<? super MouseEvent> onMousePressed;
    EventHandler<? super MouseEvent> onMouseDragged;
    OnDrag onDrag;
    boolean stayWithinBounds = true;
    double minX = 0;
    double maxX = WIDTH;
    double minY = 0;
    double maxY = GameScreen.PLAY_AREA_HEIGHT;
    boolean doNotTranslate = false;
    //handleX and handleY are offsets used strictly for the callbacks, they are not used for the translation of the destination Node.
    double handleX = 0;
    double handleY = 0;
    double X;
    double Y;
    boolean reverseX = false;
    boolean reverseY = false;
    float amountOfTargetNodeToAllowOutsideLimit = 0f;

    public void setOnDrag(OnDrag onDrag) {
        this.onDrag = onDrag;
    }

    ////////////////////////////////////////////////////////////////////////////
    //constructors
    public Draggable() {
    }

    public Draggable(double handleX, double handleY) {
        this.handleX = handleX;
        this.handleY = handleY;
    }

    ////////////////////////////////////////////////////////////////////////////
    //methods
    public void makeDraggable(Node source, Node target, double dragAdjustmentRatioX, double dragAdjustmentRatioY) {
        source.setOnMouseDragged((MouseEvent event) -> {
            if (myPressed.get() && !doNotTranslate) {
                double x = myInitialTranslates.getX() - dragInitialTranslates.getX() + dragAdjustmentRatioX * GameScreen.instance.scaleXToScreen(event.getSceneX());
                double y = myInitialTranslates.getY() - dragInitialTranslates.getY() + dragAdjustmentRatioY * GameScreen.instance.scaleYToScreen(event.getSceneY());
                double localMaxX = maxX - target.getBoundsInLocal().getWidth() * amountOfTargetNodeToAllowOutsideLimit;
                double localMinX = minX - target.getBoundsInLocal().getWidth() * amountOfTargetNodeToAllowOutsideLimit;
                if (x > localMinX && x < localMaxX) {
                    target.setTranslateX(x);
                } else {
//                    Log.debug("X is not within range.  x = " + x);
                }
                double localMaxY = maxY - target.getBoundsInLocal().getHeight() * amountOfTargetNodeToAllowOutsideLimit;
                double localMinY = minY - target.getBoundsInLocal().getWidth() * amountOfTargetNodeToAllowOutsideLimit;
                if (y > localMinY && y < localMaxY) {
                    target.setTranslateY(y);
                } else {
//                    Log.debug("Y is not within range.  x = " + y);
                }
            } else {
            }
            if (onMouseDragged != null) {
                onMouseDragged.handle(event);
            }
            if (onDrag != null) {
                if (reverseX) {
                    X = handleX + dragInitialTranslates.getX() - dragAdjustmentRatioX * GameScreen.instance.scaleXToScreen(event.getSceneX());
                } else {
                    X = handleX - dragInitialTranslates.getX() + dragAdjustmentRatioX * GameScreen.instance.scaleXToScreen(event.getSceneX());
                }
                if (reverseY) {
                    Y = handleY + dragInitialTranslates.getY() - dragAdjustmentRatioY * GameScreen.instance.scaleYToScreen(event.getSceneY());
                } else {
                    Y = handleY - dragInitialTranslates.getY() + dragAdjustmentRatioY * GameScreen.instance.scaleYToScreen(event.getSceneY());
                }
                onDrag.handle(X, Y);
            }
        });
        source.setOnMousePressed((MouseEvent event) -> {
            myInitialTranslates = new Point2D(target.getTranslateX(), target.getTranslateY());
            dragInitialTranslates = new Point2D(
                    dragAdjustmentRatioX * GameScreen.instance.scaleXToScreen(event.getSceneX()),
                    dragAdjustmentRatioY * GameScreen.instance.scaleYToScreen(event.getSceneY()));
            myPressed.set(true);
            if (onMousePressed != null) {
                onMousePressed.handle(event);
            }
            GameScreen.setCursorMove();
        });
        source.setOnMouseReleased((MouseEvent event) -> {
            myPressed.set(false);
            handleX = X;
            handleY = Y;
            if (onMouseReleased != null) {
                onMouseReleased.handle(event);
            }
            GameScreen.setCursorCustom();
        });
    }

    public void makeDraggable(Node node, double dragAdjustmentRatioX, double dragAdjustmentRatioY) {
        makeDraggable(node, node, dragAdjustmentRatioX, dragAdjustmentRatioY);
    }

    public void setOnMouseReleased(EventHandler<? super MouseEvent> onMouseReleased) {
        this.onMouseReleased = onMouseReleased;
    }

    public void setOnMousePressed(EventHandler<? super MouseEvent> onMousePressed) {
        this.onMousePressed = onMousePressed;
    }

    public void setOnMouseDragged(EventHandler<? super MouseEvent> onMouseDragged) {
        this.onMouseDragged = onMouseDragged;
    }

    public void setAmountOfTargetNodeToAllowOutsideLimit(float amountOfTargetNodeToAllowOutsideLimit) {
        this.amountOfTargetNodeToAllowOutsideLimit = amountOfTargetNodeToAllowOutsideLimit;
    }

    public interface OnDrag {

        public void handle(double x, double y);
    }

    public boolean isStayWithinBounds() {
        return stayWithinBounds;
    }

    public void setStayWithinBounds(boolean stayWithinBounds) {
        this.stayWithinBounds = stayWithinBounds;
    }

    public void setMinMax(double minX, double maxX, double minY, double maxY) {
        this.maxX = maxX;
        this.minX = minX;
        this.minY = minY;
        this.maxY = maxY;
    }

    public void setDoNotTranslate(boolean doNotTranslate) {
        this.doNotTranslate = doNotTranslate;
    }

    public boolean isReverseX() {
        return reverseX;
    }

    public void setReverseX(boolean reverseX) {
        this.reverseX = reverseX;
    }

    public boolean isReverseY() {
        return reverseY;
    }

    public void setReverseY(boolean reverseY) {
        this.reverseY = reverseY;
    }

    public double getHandleX() {
        return handleX;
    }

    public void setHandleX(double handleX) {
        this.handleX = handleX;
    }

    public double getHandleY() {
        return handleY;
    }

    public void setHandleY(double handleY) {
        this.handleY = handleY;
    }

}

GameScreen.instance.scaleXToScreen and GameScreen.instance.scaleYToScreen just set this to 1. In the future if you scale your window, you might need to adjust for it.

GameScreen.WIDTH and GameScreen.PLAY_HEIGHT set tot he width and height of your scene.

The two method calls to change the mouse cursor can be deleted.

The way you use it is like this...

new Draggable().makeDraggable(nodeYouWantToDrag, 1, 1);

and thats that.

EDIT: it also has callbacks for basic events like onDrag, onMousePressed, onMouseDragged, and onMouseReleased.

Upvotes: 1

Related Questions