Detect click in a diamond

1.5k views Asked by At

I want to detect if I click inside a diamond. The only thing I have are the coordinates of the click (x,y), the center of the diamond (x,y) and the width/height of the diamond.

I found this, but the problem is different. pixel coordinates on diamond

2

There are 2 answers

2
Marco13 On BEST ANSWER

The answer that you linked actually contains everything you need: You can do the "Direct point position check" to detect whether a point is inside the diamond.

I assume that the diamonds can not be rotated or so, otherwise, the question would have been horribly imprecise.

Here is an MCVE, implemented in Java/Swing as an example:

DiamondClickTest

The relevant part is actually the Diamond#contains method at the bottom, which consists of the 4 lines of code taken from the other answer....

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

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


public class DiamondClickTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        List<Diamond> diamonds = new ArrayList<Diamond>();
        diamonds.add(new Diamond("A", new Point(100,100), 180, 140));
        diamonds.add(new Diamond("B", new Point(300,100), 110, 160));
        diamonds.add(new Diamond("C", new Point(100,300), 110, 180));
        diamonds.add(new Diamond("D", new Point(300,300), 130, 150));

        DiamondClickTestPanel p = new DiamondClickTestPanel(diamonds);
        f.getContentPane().add(p);

        f.setSize(400,430);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

}

class DiamondClickTestPanel extends JPanel implements MouseMotionListener
{
    private List<Diamond> diamonds;
    private Diamond highlighedDiamond = null;

    DiamondClickTestPanel(List<Diamond> diamonds)
    {
        this.diamonds = diamonds;
        addMouseMotionListener(this);
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING, 
            RenderingHints.VALUE_ANTIALIAS_ON);

        for (Diamond diamond : diamonds)
        {
            draw(g, diamond);
        }
    }

    private void draw(Graphics2D g, Diamond diamond)
    {
        Point2D c = diamond.getCenter();
        double x0 = c.getX() + diamond.getWidth() * 0.5;
        double y0 = c.getY();
        double x1 = c.getX();
        double y1 = c.getY() - diamond.getHeight() * 0.5;
        double x2 = c.getX() - diamond.getWidth() * 0.5;
        double y2 = c.getY();
        double x3 = c.getX();
        double y3 = c.getY() + diamond.getHeight() * 0.5;

        Path2D p = new Path2D.Double();
        p.moveTo(x0, y0);
        p.lineTo(x1, y1);
        p.lineTo(x2, y2);
        p.lineTo(x3, y3);
        p.closePath();

        if (diamond == highlighedDiamond)
        {
            g.setColor(Color.RED);
            g.fill(p);
        }
        g.setColor(Color.BLACK);
        g.draw(p);
        g.drawString(diamond.getName(), (int)c.getX()-4, (int)c.getY()+8);
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
        double x = e.getX();
        double y = e.getY();
        highlighedDiamond = null;
        for (Diamond diamond : diamonds)
        {
            if (diamond.contains(x, y))
            {
                highlighedDiamond = diamond;
            }
        }
        repaint();
    }

}

class Diamond
{
    private String name;
    private Point2D center;
    private double width;
    private double height;

    Diamond(String name, Point2D center, double width, double height)
    {
        this.name = name;
        this.center = center;
        this.width = width;
        this.height = height;
    }

    String getName()
    {
        return name;
    }
    Point2D getCenter()
    {
        return center;
    }
    double getWidth()
    {
        return width;
    }
    double getHeight()
    {
        return height;
    }

    boolean contains(double x, double y)
    {
        double dx = Math.abs(x - center.getX());
        double dy = Math.abs(y - center.getY());
        double d = dx / width + dy / height;
        return d <= 0.5;
    }
}
2
andand On

You can formulate a distance measure based upon the l(1) norm within which points of fixed distance from some center point form an axially aligned diamond with vertices equidistant from the center.

In this case you will need to apply a suitable affine transformation to place your diamond into a canonical form centered at the origin with the vertices of the diamond placed on the coordinate axes equidistant from the origin; call this distance r. Depending upon the form of the original diamond, this may require translation (if the diamond is not centered on the origin), rotation (if the diagonals of the diamond are not axially aligned) and scaling (if the diagonals are not of equal length) operations which form the basis of the affine transformation you will apply. You then apply this same affine transformation to your mouse click and sum the absolute value of each component of the resulting point; call this sum d. If r > d then the point lies interior to the diamond. If d > r the point lies exterior to the diamond, and if r = d the point lies on an edge of the diamond.