Draven Lewis
Draven Lewis

Reputation: 57

Projecting mouse coordinates

This question has probably been answered but from the ones i have found they don't seem to work, others actually not making any changes.

I'm creating a game, the game has a screen (Canvas) that is inside of the window (JFrame). In this game i would like to use a mouse, as well as allowing the user to maximize the window. The game screen stays the same size no matter what, but if the window is re-sized, so i scale the game image to the size of the window.

My problem is that i cannot find a suitable way to project the coordinates of the the mouse, as when the window is maximized, its coordinates are still being read as if the window were still normal sized, creating an offset.

I've tried:

@Override public void mouseMoved(MouseEvent e){
    MOUSE_X = Math.round((float) e.getX() / (float) RenderableCanvas.oldSizeX * (float) f.getWidth());
    MOUSE_Y = Math.round((float) e.getY() / (float) RenderableCanvas.oldSizeY * (float) f.getHeight());
}

Where MOUSE_X/MOUSE_Y are static variables that can be referenced anywhere in the program to get the mouse location.

and RenderableCanvas is the game window, containing an embedded canvas object, this class also keeps track of the original size of the window stated as oldSizeX and oldSizeY

and finally f.getHeight() and f.getWidth() are the current size of the frame, as f is a reference to the JFrame inside of the RenderableCanvas class.

but all that does the same as:

@Override public void mouseMoved(MouseEvent e){
    MOUSE_X = e.getX();
    MOUSE_Y = e.getY();
}

Thank you for any help in advance.

Upvotes: 0

Views: 388

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347194

The basic idea is you need to be able to convert between the two coordinate systems, in this case, the "world" which is the space getting scaled and the "view" which is what the user sees (or something like that)

The basic maths is to use the default value and the current value to generate a percentage value, by which you can then multiple the target value by, for example convert from the view to the world might look like...

pointInView * (staticSize / currentSize)

So, given a point in "world" coordinates, you need to scale back to "view" coordinates...

protected Point toView(int x, int y) {
    return toView(new Point(x, y));
}

protected Point toView(Point p) {
    Point scaled = new Point(p);
    scaled.x = Math.round(p.x * ((float) getWidth() / (float) DEFAULT_WIDTH));
    scaled.y = Math.round(p.y * ((float) getHeight() / (float) DEFAULT_HEIGHT));
    return scaled;
}

And given "view" coordinates, you need to scale up to the "world" coordinates...

protected Point toWorld(int x, int y) {
    return toWorld(new Point(x, y));
}

protected Point toWorld(Point p) {
    Point scaled = new Point(p);
    scaled.x = Math.round(p.x * ((float) DEFAULT_WIDTH) / (float) getWidth());
    scaled.y = Math.round(p.y * ((float) DEFAULT_HEIGHT) / (float) getHeight());
    return scaled;
}

So, for example, when the mouse is moved or clicked on your "view", you could use

Point world = toWorld(e.getPoint());

to convert the mouse point into world coordinates

... please feel free to rename those to suit your own needs, but basically, view is the physical view that the user sees and is your virtual concept of that view...

The basic concept will work for Dimension and by extension, Rectangle as well...

Scaled World View

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        protected static final int DEFAULT_WIDTH = 200;
        protected static final int DEFAULT_HEIGHT = 200;

        private Dimension preferredSize = new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);

        private JLabel properties;
        private boolean highlighted = false;

        private Rectangle hoverRect = new Rectangle(10, 10, 50, 50);

        public TestPane() {
            setLayout(new GridBagLayout());
            properties = new JLabel("...");
            add(properties);

            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    int x = e.getX();
                    int y = e.getY();

                    Point world = toWorld(e.getPoint());

                    highlighted = hoverRect.contains(world);
                    repaint();

                    properties.setText("<html>@" + format(e.getPoint())
                            + "<br>world = " + format(world)
                            + "<br>view = " + format(toView(world)));
                }

            });

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    preferredSize = new Dimension(DEFAULT_WIDTH * 2, DEFAULT_HEIGHT * 2);
                    SwingUtilities.windowForComponent(TestPane.this).pack();
                }
            });
        }

        protected String format(Point p) {
            return p.x + "x" + p.y;
        }

        protected Point toView(int x, int y) {
            return toView(new Point(x, y));
        }

        protected Point toView(Point p) {
            Point scaled = new Point(p);
            scaled.x = Math.round(p.x * ((float) getWidth() / (float) DEFAULT_WIDTH));
            scaled.y = Math.round(p.y * ((float) getHeight() / (float) DEFAULT_HEIGHT));
            return scaled;
        }

        protected Point toWorld(int x, int y) {
            return toWorld(new Point(x, y));
        }

        protected Point toWorld(Point p) {
            Point scaled = new Point(p);
            scaled.x = Math.round(p.x * ((float) DEFAULT_WIDTH) / (float) getWidth());
            scaled.y = Math.round(p.y * ((float) DEFAULT_HEIGHT) / (float) getHeight());
            return scaled;
        }

        protected Rectangle toWorld(Rectangle bounds) {
            return toWorld(bounds.x, bounds.y, bounds.width, bounds.height);
        }

        protected Rectangle toWorld(int x, int y, int width, int height) {
            Rectangle scaled = new Rectangle();
            scaled.setLocation(toWorld(x, y));
            scaled.width = Math.round(width * ((float) DEFAULT_WIDTH / (float) getWidth()));
            scaled.height = Math.round(height * ((float) DEFAULT_HEIGHT / (float) getHeight()));
            return scaled;
        }

        protected Rectangle toView(Rectangle bounds) {
            return toView(bounds.x, bounds.y, bounds.width, bounds.height);
        }

        protected Rectangle toView(int x, int y, int width, int height) {
            Rectangle scaled = new Rectangle();
            scaled.setLocation(toView(x, y));
            scaled.width = Math.round(width * ((float) getWidth() / (float) DEFAULT_WIDTH));
            scaled.height = Math.round(height * ((float) getHeight() / (float) DEFAULT_HEIGHT));
            return scaled;
        }

        @Override
        public Dimension getPreferredSize() {
            return preferredSize;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            Rectangle bounds = toView(hoverRect);
            if (highlighted) {
                g2d.setColor(Color.BLUE);
                g2d.fill(bounds);
            }
            g2d.setColor(Color.BLACK);
            g2d.draw(bounds);
        }

    }

}

Upvotes: 1

Draven Lewis
Draven Lewis

Reputation: 57

So i think I've got it, i did some old Pen and Paper math and realized that i had part of my equation flipped from what i thought i should have.

This seems to work:

@Override public void mouseMoved(MouseEvent e){
    MOUSE_X = (int) ((float) e.getX() / (float) f.getWidth() * (float) RenderableCanvas.oldSizeX);
    MOUSE_Y = (int) ((float) e.getY() /  (float) f.getHeight() * (float) RenderableCanvas.oldSizeY);
}

Upvotes: 0

Related Questions