Search code examples
javajavafxgraphicsgeometryzooming

Zooming relative to mouse doesnt work in JavaFX


I had a working code, which used ParallelCamera when zooming, and it worked perfectly. Here's it:

double s = camera.getScaleX();
double d = e.getDeltaY();
double f = 1.2;

double x = e.getSceneX();
double y = e.getSceneY();

if (d < 0) {
    f = 2;
} else if (d > 0) {
    f = 0.5;
}

double ps = s;

/*if (s * f > 1) {
    camera.setScaleX(1);
    camera.setScaleY(1);
    camera.setScaleZ(1);
    camera.setTranslateX(0);
    camera.setTranslateY(0);
    return;
}

camera.setScaleX(s * f);
camera.setScaleY(s * f);
camera.setScaleZ(s * f);

s = camera.getScaleX();

double dx = ps * (1 - f) * x;
double dy = ps * (1 - f) * y;
                
System.out.println(dx);

double width = primaryScene.getWidth();
double height = primaryScene.getHeight();

if (camera.getTranslateX() + s * width + dx > width) {
    camera.setTranslateX(width - s * width);
} else if ((camera.getTranslateX() + dx < 0)) {
    camera.setTranslateX(0);
} else {
    camera.setTranslateX(camera.getTranslateX() + dx);
}

if (camera.getTranslateY() + s * height + dy > height) {
    camera.setTranslateY(height - s * height);
} else if ((camera.getTranslateY() + dy < 0)) {
    camera.setTranslateY(0);
} else {
    camera.setTranslateY(camera.getTranslateY() + dy);
}

But I wanted to not have the quality loss, so I rewrote it. Now it scales the Pane, and not the camera. I followed exactly the same code, only this time the scaling is reversed. And when I calculate the position, I first set an offset tomimic my original code. (The camera's pivot point is at 0,0, but the Pane's is in the center) The quality loss is gone, but it doesnt really work:

double s = mapLayer.getScaleX();
double d = e.getDeltaY();
double f = 1.2;
                
double width = primaryScene.getWidth();
double height = primaryScene.getHeight();

double x = (e.getSceneX());
double y = (e.getSceneY());

if (d > 0) {
    f = 2;
} else{
    f = 0.5;
}

double ps = s;

mapLayer.setScaleX(s * f);
mapLayer.setScaleY(s * f);
mapLayer.setScaleZ(s * f);
                
            
s = mapLayer.getScaleX();

double dx = ps * (1 - f) * x;
double dy = ps * (1 - f) * y;

mapLayer.setTranslateX(mapLayer.getTranslateX()+ps*(width/2)*(f-1));
mapLayer.setTranslateY(mapLayer.getTranslateY()+ps*(height/2)*(f-1));
mapLayer.setTranslateX(mapLayer.getTranslateX()+dx);
mapLayer.setTranslateY(mapLayer.getTranslateY()+dy);

Solution

  • I found this, and it worked like a charm: JavaFX 8 - Zooming Relative to Mouse Pointer. The solution now is this (without clamping):

    double delta = 1.2;
    double scale = mapLayer.getScaleX(); // currently we only use Y, same value is used for X
    double oldScale = scale;
    if (e.getDeltaY() < 0) {
        scale /= delta;
    } else {
        scale *= delta;
    }
    double f = (scale / oldScale) - 1;
    double dx = (e.getSceneX()
        - (mapLayer.getBoundsInParent().getWidth() / 2
        + mapLayer.getBoundsInParent().getMinX()));
    double dy = (e.getSceneY()
        - (mapLayer.getBoundsInParent().getHeight() / 2
        + mapLayer.getBoundsInParent().getMinY()));
    
    mapLayer.setScaleX(scale);
    mapLayer.setScaleY(scale);
    mapLayer.setScaleZ(scale);
    
    mapLayer.setTranslateX(mapLayer.getTranslateX() - f * dx);
    mapLayer.setTranslateY(mapLayer.getTranslateY() - f * dy);