JavaFx zooming to mouse as pivot -
i have tried example given in another post learn zooming , panning relative mouse pointer. when on grid, zooming works expected:
when zooming mouse pointer location on top left image, zoomed exact location seen in top right image.
if dragged off grid, e.g. pivot starts 'misbehave':
when zooming mouse pointer location on bottom left image, zoomed location other 1 intended, seen in bottom right image.
the bounds of canvas inside parent changes 600x600 (without scale) 600x700… affects outcomes dx, dy of following function.
double dx = (event.getscenex() - (canvas.getboundsinparent().getwidth()/2 + canvas.getboundsinparent().getminx())); double dy = (event.getsceney() - (canvas.getboundsinparent().getheight()/2 + canvas.getboundsinparent().getminy())); when editing function changing .getwidth() .getheight() , again move rectangle out right… zoom works correctly. however, if rectangle moved out vertically (to bottom or top) , left problem again reproduced again.
is above function correct, trying do? why zoom not work same, when on grid?
import javafx.application.application; import javafx.beans.property.doubleproperty; import javafx.beans.property.simpledoubleproperty; import javafx.event.eventhandler; import javafx.scene.group; import javafx.scene.node; import javafx.scene.scene; import javafx.scene.canvas.canvas; import javafx.scene.canvas.graphicscontext; import javafx.scene.control.label; import javafx.scene.input.mouseevent; import javafx.scene.input.scrollevent; import javafx.scene.layout.pane; import javafx.scene.paint.color; import javafx.scene.shape.circle; import javafx.scene.shape.rectangle; import javafx.stage.stage; class pannablecanvas extends pane { doubleproperty myscale = new simpledoubleproperty(1.0); public pannablecanvas() { setprefsize(600, 600); setstyle("-fx-background-color: lightgrey; -fx-border-color: blue;"); // add scale transform scalexproperty().bind(myscale); scaleyproperty().bind(myscale); } /** * add grid canvas, send */ public void addgrid() { double w = getboundsinlocal().getwidth(); double h = getboundsinlocal().getheight(); // add grid canvas grid = new canvas(w, h); // don't catch mouse events grid.setmousetransparent(true); graphicscontext gc = grid.getgraphicscontext2d(); gc.setstroke(color.gray); gc.setlinewidth(1); // draw grid lines double offset = 50; for( double i=offset; < w; i+=offset) { gc.strokeline( i, 0, i, h); gc.strokeline( 0, i, w, i); } getchildren().add( grid); grid.toback(); } public double getscale() { return myscale.get(); } public void setscale( double scale) { myscale.set(scale); } public void setpivot( double x, double y) { settranslatex(gettranslatex()-x); settranslatey(gettranslatey()-y); } } /** * mouse drag context used scene , nodes. */ class dragcontext { double mouseanchorx; double mouseanchory; double translateanchorx; double translateanchory; } /** * listeners making nodes draggable via left mouse button. considers if parent zoomed. */ class nodegestures { private dragcontext nodedragcontext = new dragcontext(); pannablecanvas canvas; public nodegestures( pannablecanvas canvas) { this.canvas = canvas; } public eventhandler<mouseevent> getonmousepressedeventhandler() { return onmousepressedeventhandler; } public eventhandler<mouseevent> getonmousedraggedeventhandler() { return onmousedraggedeventhandler; } private eventhandler<mouseevent> onmousepressedeventhandler = new eventhandler<mouseevent>() { public void handle(mouseevent event) { // left mouse button => dragging if( !event.isprimarybuttondown()) return; nodedragcontext.mouseanchorx = event.getscenex(); nodedragcontext.mouseanchory = event.getsceney(); node node = (node) event.getsource(); nodedragcontext.translateanchorx = node.gettranslatex(); nodedragcontext.translateanchory = node.gettranslatey(); } }; private eventhandler<mouseevent> onmousedraggedeventhandler = new eventhandler<mouseevent>() { public void handle(mouseevent event) { // left mouse button => dragging if( !event.isprimarybuttondown()) return; double scale = canvas.getscale(); node node = (node) event.getsource(); node.settranslatex(nodedragcontext.translateanchorx + (( event.getscenex() - nodedragcontext.mouseanchorx) / scale)); node.settranslatey(nodedragcontext.translateanchory + (( event.getsceney() - nodedragcontext.mouseanchory) / scale)); event.consume(); } }; } /** * listeners making scene's canvas draggable , zoomable */ class scenegestures { private static final double max_scale = 10.0d; private static final double min_scale = .1d; private dragcontext scenedragcontext = new dragcontext(); pannablecanvas canvas; public scenegestures( pannablecanvas canvas) { this.canvas = canvas; } public eventhandler<mouseevent> getonmousepressedeventhandler() { return onmousepressedeventhandler; } public eventhandler<mouseevent> getonmousedraggedeventhandler() { return onmousedraggedeventhandler; } public eventhandler<scrollevent> getonscrolleventhandler() { return onscrolleventhandler; } private eventhandler<mouseevent> onmousepressedeventhandler = new eventhandler<mouseevent>() { public void handle(mouseevent event) { // right mouse button => panning if( !event.issecondarybuttondown()) return; scenedragcontext.mouseanchorx = event.getscenex(); scenedragcontext.mouseanchory = event.getsceney(); scenedragcontext.translateanchorx = canvas.gettranslatex(); scenedragcontext.translateanchory = canvas.gettranslatey(); } }; private eventhandler<mouseevent> onmousedraggedeventhandler = new eventhandler<mouseevent>() { public void handle(mouseevent event) { // right mouse button => panning if( !event.issecondarybuttondown()) return; canvas.settranslatex(scenedragcontext.translateanchorx + event.getscenex() - scenedragcontext.mouseanchorx); canvas.settranslatey(scenedragcontext.translateanchory + event.getsceney() - scenedragcontext.mouseanchory); event.consume(); } }; /** * mouse wheel handler: zoom pivot point */ private eventhandler<scrollevent> onscrolleventhandler = new eventhandler<scrollevent>() { @override public void handle(scrollevent event) { double delta = 1.2; double scale = canvas.getscale(); // use y, same value used x double oldscale = scale; if (event.getdeltay() < 0) scale /= delta; else scale *= delta; scale = clamp( scale, min_scale, max_scale); double f = (scale / oldscale)-1; double dx = (event.getscenex() - (canvas.getboundsinparent().getwidth()/2 + canvas.getboundsinparent().getminx())); double dy = (event.getsceney() - (canvas.getboundsinparent().getheight()/2 + canvas.getboundsinparent().getminy())); canvas.setscale( scale); // note: pivot value must untransformed, i. e. without scaling canvas.setpivot(f*dx, f*dy); event.consume(); } }; public static double clamp( double value, double min, double max) { if( double.compare(value, min) < 0) return min; if( double.compare(value, max) > 0) return max; return value; } } /** * application zoomable , pannable canvas. */ public class zoomandscrollapplication extends application { public static void main(string[] args) { launch(args); } @override public void start(stage stage) { group group = new group(); // create canvas pannablecanvas canvas = new pannablecanvas(); // don't want canvas on top/left in example => // translate bit canvas.settranslatex(100); canvas.settranslatey(100); // create sample nodes can dragged nodegestures nodegestures = new nodegestures( canvas); label label1 = new label("draggable node 1"); label1.settranslatex(10); label1.settranslatey(10); label1.addeventfilter( mouseevent.mouse_pressed, nodegestures.getonmousepressedeventhandler()); label1.addeventfilter( mouseevent.mouse_dragged, nodegestures.getonmousedraggedeventhandler()); label label2 = new label("draggable node 2"); label2.settranslatex(100); label2.settranslatey(100); label2.addeventfilter( mouseevent.mouse_pressed, nodegestures.getonmousepressedeventhandler()); label2.addeventfilter( mouseevent.mouse_dragged, nodegestures.getonmousedraggedeventhandler()); label label3 = new label("draggable node 3"); label3.settranslatex(200); label3.settranslatey(200); label3.addeventfilter( mouseevent.mouse_pressed, nodegestures.getonmousepressedeventhandler()); label3.addeventfilter( mouseevent.mouse_dragged, nodegestures.getonmousedraggedeventhandler()); circle circle1 = new circle( 300, 300, 50); circle1.setstroke(color.orange); circle1.setfill(color.orange.derivecolor(1, 1, 1, 0.5)); circle1.addeventfilter( mouseevent.mouse_pressed, nodegestures.getonmousepressedeventhandler()); circle1.addeventfilter( mouseevent.mouse_dragged, nodegestures.getonmousedraggedeventhandler()); rectangle rect1 = new rectangle(100,100); rect1.settranslatex(450); rect1.settranslatey(450); rect1.setstroke(color.blue); rect1.setfill(color.blue.derivecolor(1, 1, 1, 0.5)); rect1.addeventfilter( mouseevent.mouse_pressed, nodegestures.getonmousepressedeventhandler()); rect1.addeventfilter( mouseevent.mouse_dragged, nodegestures.getonmousedraggedeventhandler()); canvas.getchildren().addall(label1, label2, label3, circle1, rect1); group.getchildren().add(canvas); // create scene can dragged , zoomed scene scene = new scene(group, 1024, 768); scenegestures scenegestures = new scenegestures(canvas); scene.addeventfilter( mouseevent.mouse_pressed, scenegestures.getonmousepressedeventhandler()); scene.addeventfilter( mouseevent.mouse_dragged, scenegestures.getonmousedraggedeventhandler()); scene.addeventfilter( scrollevent.any, scenegestures.getonscrolleventhandler()); stage.setscene(scene); stage.show(); canvas.addgrid(); } }
as nobody answered question until , stumbled on same problem, post solution, adds simple calculation of left/up/lower , right overhang of nodes.
if replace part of zooming-code part attached below, should got.
//maxx = right overhang, maxy = lower overhang double maxx = canvas.getboundsinparent().getmaxx() - canvas.localtoparent(canvas.getprefwidth(), canvas.getprefheight()).getx(); double maxy = canvas.getboundsinparent().getmaxy() - canvas.localtoparent(canvas.getprefwidth(), canvas.getprefheight()).gety(); // minx = left overhang, miny = upper overhang double minx = canvas.localtoparent(0,0).getx() - canvas.getboundsinparent().getminx(); double miny = canvas.localtoparent(0,0).gety() - canvas.getboundsinparent().getminy(); // adding overhangs together, consider width of canvas double subx = maxx + minx; double suby = maxy + miny; // subtracting overall overhang width , left , upper overhang upper left point double dx = (event.getscenex() - ((canvas.getboundsinparent().getwidth()-subx)/2 + (canvas.getboundsinparent().getminx()+minx))); double dy = (event.getsceney() - ((canvas.getboundsinparent().getheight()-suby)/2 + (canvas.getboundsinparent().getminy()+miny))); warning: left , overhang computed correctly, did not find working way, compute right , lower overhang of nodes without use of preferred height , width attributes. keep in mind, need these.
also, can improve performance computing canvas.getboundsinparent() thing once before the other calculations computed multiple times.
hope helps someone.

Comments
Post a Comment