Sensing hover over outer edge of a Path2D circle in JPanel [duplicate]

给你一囗甜甜゛ 提交于 2020-01-11 13:54:07

问题


So, I have a program that draws Path2D circles onto JPanel. What I am trying to do is resize the circle when the user clicks and drags the bottom right of the circle. So, what I want is to detect when they are on the bottom-right outer edge of the circle, not the bottom-right of the bounds around the circle. Basically, I need to figure out how to do something like this:

I know how to do this with rectangles using getBounds() but when you use getBounds() on a circle, it will return the square around the circle and not the bounds of the actual circle. Any ideas on how I can get this to work? Thanks!

Here is a shortened, runnable version of my program:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;

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

public class Editor {

    public static void main(String[] args) {

        JFrame frame = new UMLWindow();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setBounds(30, 30, 1000, 700);
        frame.getContentPane().setBackground(Color.white);
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

class UMLWindow extends JFrame {
    Shapes shapeList = new Shapes();
    Panel panel;

    private static final long serialVersionUID = 1L;

    public UMLWindow() {
        addMenus();
        panel = new Panel();
    }

    public void addMenus() {

        getContentPane().add(shapeList);

        setSize(300, 200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

        shapeList.addCircle(100, 100);
    }
}

// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
class Shapes extends JPanel {
    private static final long serialVersionUID = 1L;

    private List<Path2D> shapes = new ArrayList<Path2D>();
    int currentIndex;

    public Shapes() {
        MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
        addMouseListener(myMouseAdapter);
        addMouseMotionListener(myMouseAdapter);
    }

    public void addCircle(int width, int height) {
        Path2D circ = new Path2D.Double();
        circ.append(new Ellipse2D.Double(442, 269, width, height), true);
        shapes.add(circ);
        repaint();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g;
        g2.setStroke(new BasicStroke(2));
        for (Path2D shape : shapes) {
            g2.draw(shape);
        }
    }

    class MyMouseAdapter extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {

        }

        @Override
        public void mouseDragged(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {
        }
    }
}

回答1:


You'll want to brush up your trig (or google search like I did ;)). The basic concept is "relatively" easy, but I created a nice method to all the work for me...

This method...

public Point2D getPointOnEdge(float angel, Rectangle bounds) {

    float radius = Math.max(bounds.width, bounds.height) / 2;

    float x = radius;
    float y = radius;

    double rads = Math.toRadians((angel + 90));

    // Calculate the outter point of the line
    float xPosy = (float) (x + Math.cos(rads) * radius);
    float yPosy = (float) (y + Math.sin(rads) * radius);

    return new Point2D.Float(xPosy + bounds.x, yPosy + bounds.y);

}

Will calculate the x/y point that a given angle will appear on a circle, remember, this will ONLY work for circles!

I then use another method...

public Rectangle2D getActiveBounds(float angel, Rectangle bounds) {

    Point2D p = getPointOnEdge(angel, bounds);

    return new Rectangle2D.Double(p.getX() - 4, p.getY() - 4, 8, 8);

}

To calculate the "mouse zone" in which I would consider to be the bottom/right area, cause a single pixel is hard to find and simply use Rectangle#contains, passing it the current mouse location...

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Editor {

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

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

                JFrame frame = new UMLWindow();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setBounds(30, 30, 1000, 700);
                frame.getContentPane().setBackground(Color.white);
                frame.setVisible(true);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class UMLWindow extends JFrame {

        Shapes shapeList = new Shapes();
        Panel panel;

        private static final long serialVersionUID = 1L;

        public UMLWindow() {
            addMenus();
            panel = new Panel();
        }

        public void addMenus() {

            getContentPane().add(shapeList);

            setSize(300, 200);
            setLocationRelativeTo(null);
            setDefaultCloseOperation(EXIT_ON_CLOSE);

            shapeList.addCircle(100, 100);
        }
    }

// Shapes class, used to draw the shapes on the panel
// as well as implements the MouseListener for dragging
    public static class Shapes extends JPanel {

        private static final long serialVersionUID = 1L;

        private List<Path2D> shapes = new ArrayList<Path2D>();
        int currentIndex;

        private Point mousePoint;

        public Shapes() {
            MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
            addMouseListener(myMouseAdapter);
            addMouseMotionListener(myMouseAdapter);
        }

        public void addCircle(int width, int height) {
            Path2D circ = new Path2D.Double();
            circ.append(new Ellipse2D.Double(442, 269, width, height), true);
            shapes.add(circ);
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2 = (Graphics2D) g;
            g2.setStroke(new BasicStroke(2));
            for (Path2D shape : shapes) {
                g2.setColor(Color.BLACK);
                g2.draw(shape);

                g2.setColor(Color.RED);
                Rectangle2D bottomRight = getActiveBounds(-45, shape.getBounds());

                g2.draw(bottomRight);
                if (mousePoint != null) {

                    if (bottomRight.contains(mousePoint)) {
                        g2.fill(bottomRight);
                    }

                }

            }
        }

        public Rectangle2D getActiveBounds(float angel, Rectangle bounds) {

            Point2D p = getPointOnEdge(angel, bounds);

            return new Rectangle2D.Double(p.getX() - 4, p.getY() - 4, 8, 8);

        }

        public Point2D getPointOnEdge(float angel, Rectangle bounds) {

            float radius = Math.max(bounds.width, bounds.height) / 2;

            float x = radius;
            float y = radius;

            double rads = Math.toRadians((angel + 90));

            // Calculate the outter point of the line
            float xPosy = (float) (x + Math.cos(rads) * radius);
            float yPosy = (float) (y + Math.sin(rads) * radius);

            return new Point2D.Float(xPosy + bounds.x, yPosy + bounds.y);

        }

        class MyMouseAdapter extends MouseAdapter {

            @Override
            public void mouseMoved(MouseEvent e) {
                mousePoint = e.getPoint();
                repaint();
            }

        }
    }

}

This example does all the work within the paint method, because I wanted to see the "area of effect", you can easily use the same logic to change the mouse cursor within the MouseMoitionListener




回答2:


Not sure if this will work but you can try something like:

  1. Create a Shape circle that is a couple of pixels smaller than your original Shape
  2. Create a Shape circle that is a couple of pixels larger than your original Shape
  3. Create an Area object using the larger Shape
  4. Create an Area object using the smaller Shape and subtract this Area from the larger Area
  5. Use the contains(...) method to determine if the mouse point is within this Area.


来源:https://stackoverflow.com/questions/26456405/sensing-hover-over-outer-edge-of-a-path2d-circle-in-jpanel

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!