Lasnik
Lasnik

Reputation: 254

Adding a force based on mouse position

I want a point to move towards the mouse position. Currently the point just is the mouse location. But I want it so that the bigger the distance between mouse and center of the screen, the less force get´s added to the point. So if the mouse is enough distance away from the center, the point will just stop moving further outwards because it´s force away from the center is to small. The center should always be the point which is looked at. So no repositioning.

I am really stuck here because I am pretty young and I didn´t have had vector calculation in school (I think this is what you need here). I have no idea how to do what I am trying to archive. The only thing I tried was using the distance between center and mouse (Math.hypot(width / 2 - mouseX , height / 2 - mouseY)). But it didn´t work.

This is my minimal reproducible example:

import javax.swing.*;
import java.awt.*;

public class Example extends JPanel {
    private static final int size = 500;

public Example() {
    this.setPreferredSize(new Dimension(size, size));
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    g2d.fillOval(getWidth() / 2 - 3, getHeight() / 2 - 3, 6, 6); // center
    Point point = MouseInfo.getPointerInfo().getLocation();
    SwingUtilities.convertPointFromScreen(point, this);
    // Do calculations with the point here
    g2d.fillOval(point.x - 5, point.y - 5, 10, 10);
    repaint();
}

private static void createFrame() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.add(new Example());
    f.pack();
    f.setVisible(true);
}

    public static void main(String[] args) {
        EventQueue.invokeLater(Example::createFrame);
    }
}

It would look like this:

grafik.png

Upvotes: 3

Views: 106

Answers (2)

Gilbert Le Blanc
Gilbert Le Blanc

Reputation: 51565

I modified your code somewhat to create the following GUI.

Mouse Move Example

The circle will follow your mouse around the drawing JPanel.

The first thing I did was create a Circle class. This class holds the center point, radius, and color of the circle. I use a Point2D class to hold the center point so I can move the circle in fractional pixel increments. This makes the animation smoother.

Next, I used your code to create a JFrame and a drawing JPanel. I like to separate the JFrame code from the JPanel code. It makes it easier for me to focus on one Swing component at a time.

The drawing JPanel draws the circle. Period. The controller classes are responsible for calculating the new position of the circle and calling the repaint method of the drawing JPanel.

I created a MouseMotionListener to listen for the mouse movement. This class keeps track of the mouse position.

I created a Swing Timer to animate the motion of the circle.

I used polar geometry to calculate the direction that the circle moves. The polar geometry is in the MoveListener class, the actionPerformed method. I use an arc-tangent to calculate the angle to the mouse position in radians, then take the cosine to get the X movement, and the sine to get the Y movement.

Here's the complete runnable code.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class MouseMoveExample implements Runnable {
    
    public static void main(String[] args) {
        EventQueue.invokeLater(new MouseMoveExample());
    }

    private final int size = 500;
    
    private Circle circle;
    
    private DrawingPanel drawingPanel;
    
    public MouseMoveExample() {
        this.circle = new Circle(new Point(size / 2, size / 2), 12, 
                Color.BLACK);
    }
    
    @Override
    public void run() {
        JFrame f = new JFrame("Mouse Move Example");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        this.drawingPanel = new DrawingPanel();
        f.add(drawingPanel);
        
        f.pack();
        f.setLocationByPlatform(true);
        f.setVisible(true);
    }
    
    public class DrawingPanel extends JPanel {
        
        private static final long serialVersionUID = 1L;

        public DrawingPanel() {
            this.setBackground(Color.WHITE);
            this.setPreferredSize(new Dimension(size, size));
            this.addMouseMotionListener(new MoveListener());
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(circle.getColor());
            Point centerPoint = circle.getCenterPoint();
            int radius = circle.getRadius();
            int diameter = radius + radius;
            g2d.fillOval(centerPoint.x - radius, centerPoint.y -  radius, 
                    diameter, diameter); 
        }
        
    }
    
    public class MoveListener extends MouseAdapter {
        
        private MoveTimer moveTimer;
        
        private Timer timer;
        
        public MoveListener() {
            this.moveTimer = new MoveTimer();
            this.timer = new Timer(20, moveTimer);
            this.timer.start();
        }

        @Override
        public void mouseMoved(MouseEvent event) {
            moveTimer.setEventPoint(event.getPoint());
        }
        
    }
    
    public class MoveTimer implements ActionListener {
        
        private Point eventPoint;

        public void setEventPoint(Point eventPoint) {
            this.eventPoint = eventPoint;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            Point centerPoint = circle.getCenterPoint();
            if (eventPoint != null) {
                double theta = Math.atan2(eventPoint.y - centerPoint.y, 
                        eventPoint.x - centerPoint.x);
                int increment = 1;
                double x = Math.cos(theta) * increment;
                double y = Math.sin(theta) * increment;
                circle.incrementCenterPoint(x, y);
                drawingPanel.repaint();
            }
        }   
        
    }
    
    public class Circle {
        
        private final int radius;
        
        private final Color color;
        
        private Point2D centerPoint;

        public Circle(Point centerPoint, int radius, Color color) {
            setCenterPoint(centerPoint);
            this.radius = radius;
            this.color = color;
        }

        public Point getCenterPoint() {
            return new Point((int) Math.round(centerPoint.getX()), 
                    (int) Math.round(centerPoint.getY()));
        }

        public void setCenterPoint(Point centerPoint) {
            this.centerPoint = new Point2D.Double(
                    centerPoint.x, centerPoint.y);
        }
        
        public void incrementCenterPoint(double x, double y) {
            centerPoint.setLocation(centerPoint.getX() + x, 
                    centerPoint.getY() + y);
        }

        public int getRadius() {
            return radius;
        }

        public Color getColor() {
            return color;
        }
        
    }

}

Upvotes: 0

Roie Shlosberg
Roie Shlosberg

Reputation: 219

You can declare an attraction ratio value, which will strech the circle to the center of the screen, based on its value. You need also be able to know for each cursor position how far it is from the center in percentages.

import javax.swing.*;
import java.awt.*;

public class Example extends JPanel {
    private static final int SIZE = 500;
    private static final int CENTER_CIRCLE_RADIUS = 3;
    private static final int CIRCLE_RADIUS = 5;
    private static final double ATTRACTION_RATIO = 0.75;

    public Example() {
        this.setPreferredSize(new Dimension(SIZE, SIZE));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        final int xCenterPoint = getWidth() / 2;
        final int yCenterPoint = getHeight() / 2;

        g2d.fillOval(xCenterPoint - CENTER_CIRCLE_RADIUS, yCenterPoint - CENTER_CIRCLE_RADIUS,
                CENTER_CIRCLE_RADIUS * 2, CENTER_CIRCLE_RADIUS * 2); // center
        Point point = MouseInfo.getPointerInfo().getLocation();
        SwingUtilities.convertPointFromScreen(point, this);

        final double xNearToCenter = 1 - ((double) (xCenterPoint - point.x) / xCenterPoint);
        final double yNearToCenter = 1 - ((double) (yCenterPoint - point.y) / yCenterPoint);

        g2d.fillOval((int) (xCenterPoint * (ATTRACTION_RATIO + xNearToCenter * (1 - ATTRACTION_RATIO))),
                (int) (yCenterPoint * (ATTRACTION_RATIO + yNearToCenter * (1 - ATTRACTION_RATIO))),
                CIRCLE_RADIUS * 2, CIRCLE_RADIUS * 2);

        repaint();
    }

    private static void createFrame() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Example panel = new Example();
        f.add(panel);
        f.pack();
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(Example::createFrame);
    }
}

Upvotes: 1

Related Questions