My math is failing me [SOLVED]

My guiNode is translated and scaled to adjust a 1080p frame into any Display resolution and aspect ratio.

I’d like to add a zoom effect but my math is failing me (I can’t zoom reliably on a position regardless of the current resolution).

This is my code:

public class HelloJME3 extends SimpleApplication {

    public static void main(String[] args) {
        HelloJME3 app = new HelloJME3();
        app.setShowSettings(false);
        AppSettings settings = new AppSettings(true);
        //settings.setResolution(320, 180);
        //settings.setResolution(800, 450);
        //settings.setResolution(1024, 576);
        //settings.setResolution(1024, 768);
        settings.setResolution(1280, 720); //       16/9
        //settings.setResolution(1280, 800); //     16/10
        //settings.setResolution(1280, 960); //     4/3
        //settings.setResolution(1280, 1024); //    5/4
        //settings.setResolution(1366, 768);
        //settings.setResolution(1600, 900);
        //settings.setResolution(1920, 1080);
        //settings.setFullscreen(true);
        settings.setSettingsDialogImage("Interface/splash2.jpg");
        app.setSettings(settings);

        app.start(); // start the game
    }

    @Override
    public void simpleInitApp() {
        stateManager.attach(new ZoommAppState());
    }

    class ZoommAppState extends BaseAppState {

        /*
    
    -------------
    |           |
    |           |
    |           |
    -------------

    -------------------------------
    |                             |
    |                             |
  Hz|                             |  (H zoomed)
    |                             |
  Hr-------------                 |  (H real)
    |           |                 |
    |           |                 |
    |           |                 |
    -------------------------------
    
    Hz = Hr * scale
    Hr = Hz / scale
    
    (Hz-Hr) = (Hr*scale) - Hr
    
    
         */
        @Override
        protected void initialize(Application app) {
            baseScale = 1/1.5f;
            baseTranslationY = 0;
            System.out.println("Local scale is " + baseScale);
            System.out.println("traslation y " + baseTranslationY);

            Box b = new Box(1500, 10, 1); // create cube shape

            Geometry geom = new Geometry("Box", b);  // create cube geometry from the shape
            Material mat = new Material(assetManager,
                    "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
            mat.setColor("Color", ColorRGBA.Red);   // set color of material to blue
            geom.setMaterial(mat);                   // set the cube's material
            geom.move(0, targetY, 99990);
            ((SimpleApplication) getApplication()).getGuiNode().attachChild(geom);              // make         

            for (int i = 0; i < 72; i++) {
                Box bo = new Box(1500, 1, 1);
                Geometry geom2 = new Geometry("Box", bo);  // create cube geometry from the shape
                Material mat2 = new Material(assetManager,
                        "Common/MatDefs/Misc/Unshaded.j3md");  // create a simple material
                mat2.setColor("Color", ColorRGBA.Green);   // set color of material to blue
                geom2.setMaterial(mat2);                   // set the cube's material
                geom2.move(0, i * 10, 99990);
                ((SimpleApplication) getApplication()).getGuiNode().attachChild(geom2);              // make         
            }
        }

        float maxZoom = 1.5f;
        //float targetY = 0;
        float targetY = 720 / 2;

        float baseTranslationY;
        float baseScale;
        float scale;
        float tTPF;

        @Override
        public void update(float tpf) {
            tTPF += tpf;
            if (tTPF < FastMath.PI) {
                float varScale = FastMath.sin(tTPF) * maxZoom;
                float zoomEffect = 1f + varScale;
                //adatto lo zoom all'aspect ratio
                scale = baseScale * zoomEffect;
                ((SimpleApplication) getApplication()).getGuiNode().setLocalScale(scale);
                ((SimpleApplication) getApplication()).getGuiNode().setLocalTranslation(0, baseTranslationY - ((targetY * scale) - targetY) / baseScale, 0);
            } else {
                tTPF = 0;
                //setEnabled(false);
            }
        }

        @Override
        protected void cleanup(Application app) {
        }

        @Override
        protected void onEnable() {
        }

        @Override
        protected void onDisable() {
            //System.out.println("traslation y " + baseTranslationY);
            //((SimpleApplication) getApplication()).getGuiNode().setLocalScale(baseScale);
            //((SimpleApplication) getApplication()).getGuiNode().setLocalTranslation(0, baseTranslationY, 0);
        }

    }

}

Any help is appreciated, thanks!

So this isn’t necessarily an answer but a tool that can help you.

Make a series of numbers up that are the values you expect for certain zoom levels. Then using Wolfram Alpha you can generate an equation to use to get the right values.

Like I said. Not exaaaaaaactly and answer but a tool that might help you get the answer you need.

1 Like

Reading quickly: remember that translation is always in parent space. So if you are scaling the guiNode then it should not affect translation.

I saw some * scale stuff in your translation code and if scale was needed at all in translation then it would usually be part of an unscaling which would be inverted.

2 Likes

I’m not sure from the question, but I’m assuming that goal here is for the red rectangle to stay in place?

In that case, as I understand there are three operations involved:

  • translate to have the view centered on the object
  • scale
  • translate back

To simplify you’re using only the y-axis, so those three operations would sum up to:

  • translate ( targetY )
  • N/A (we’re centered, so scaling does not change translation)
  • translate ( - targetY * scale ) (since we’re scaled, translation vector in multiplied by scale)

that comes to:

((SimpleApplication) getApplication()).getGuiNode().setLocalTranslation(0, baseTranslationY - ((targetY * scale) - targetY), 0);

So it seems to me that you unnecesarily divided by baseScale (you already took baseScale into account in calculating scale).

I’m not sure what exactly baseTranslationY should be used as it is not used anywhere (aside from System.out) and is always zero, but I guess it doesn’t change or break the math here.

1 Like

Many thanks, first tests looks good.
Explanation for the baseTranslationY: I want to adapt the image to the screen resolution, and also to the aspect ratio. When not running on a 16/9 screen the user will see black bands:

        guiNode.attachChild(scaledGuiNode);
        float scalingFactor = Display.getWidth() / 1920f;
        scaledGuiNode.scale(scalingFactor); //not correct however...
        //scaledGuiNode.scale(scalingFactor, scalingFactor, 1); ...this breaks mouse input!?
        int hOffset = (int) ((Display.getHeight() - (1080f * scalingFactor)) / 2);
        if (hOffset != 0) {
            Geometry g = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Geometry gtop = new Geometry("blackbg", new Quad(Display.getWidth(), hOffset));
            Material matBg = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
            matBg.setColor("Color", ColorRGBA.Black);
            g.setMaterial(matBg);
            g.move(0, 0, 49);
            guiNode.attachChild(g);
            gtop.setMaterial(matBg);
            gtop.move(0, Display.getHeight() - hOffset, 49);
            guiNode.attachChild(gtop);
            scaledGuiNode.setLocalTranslation(0, hOffset, 0);
        }

…so the baseTranslationY is the offset of the image (above the lower black band).

Edit: the final formula is:

.setLocalTranslation(0, baseTranslationY - ((targetY * scale) - targetY * baseScale), 0);

Thanks all! :slight_smile: