确定一个形状是否在另一个形状之上

Determine if a shape is above another shape

我正在尝试确定一个四​​边形是否在另一个四边形之上,并返回未覆盖的数量的比率。我对 "uncovered" 的定义是,如果在 y 轴上被检查的形状上方没有其他形状,并且它们在 x 轴上相交了多少。

示例:

在上图中,期望的输出应该是 0.6363。在 110 个像素中,有 70 个与其上方的对象相交,并且 70/110=0.6363

另一个例子:

在此示例中,所需的输出为 0.3636。

到目前为止我尝试的是从 double out = 1; 开始,然后如果形状 2 的 y 轴小于形状 1,我减去两个形状在 x 轴上相交的量来自 out-=(c.getX2()-b.getX2())/Math.abs(c.getX1()-c.getX2());

的某些变体的比率

但是,这似乎不起作用,而且我尝试更正代码似乎只是增加了越来越多不必要的复杂性。我假设有一种更简单的方法来做我想做的事情,但我对几何学不是很好。

c 是当前正在检查的形状,b 是与之比较的形状。

if(((b.getX2() < c.getX2()) && (b.getX2()>c.getX1()))||((b.getX2()>c.getX2())&&(b.getX2()<c.getX1()))||((b.getX1()>c.getX2())&&(b.getX1()<c.getX1()))) {
    if(b.getY2() < c.getY2()) {
        if((b.getX2() < c.getX2()) && (b.getX2()>c.getX1())) {
            out-= (c.getX2()-b.getX2())/Math.abs(c.getX1()-c.getX2());
        }
        if(((b.getX2()>c.getX2())&&(b.getX2()<c.getX1()))) {
            out-=(b.getX2()-c.getX2())/Math.abs(c.getX1()-c.getX2());
        }
        if(((b.getX1()>c.getX2())&&(b.getX1()<c.getX1()))) {
            out-=(c.getX2()-b.getX2())/Math.abs(c.getX1()-c.getX2());
        }
    }
}

我认为你的方法太复杂了,你可能会迷失在所有不同的可能条件和配置中。

如果我没理解错的话,那么最简单的解决方案就是根据两个形状的最小和最大 X 值进行计算。


旁注:

您没有说明您的 "shape" 对象 bc 是什么类型。从您调用的方法来看,它们可能是 java.awt.geom.Line2D 个对象,但实际上并不是 "four sided"。在任何情况下,您都可以将最小值和最大值计算为

double bMinX = Math.min(b.getX1(), b.getX2());
double bMaxX = Math.max(b.getX1(), b.getX2());
double cMinX = Math.min(c.getX1(), c.getX2());
double cMaxX = Math.max(c.getX1(), c.getX2());

在下面的程序中,我使用实际的 Shape 对象并调用 getBounds2D 方法来获取边界框,它可以方便地提供这些 min/max 值。但您也可以手动执行此操作。


当您拥有这些 minimum/maxmimum 值时,您可以排除一个对象完全位于另一个对象左侧或完全位于另一个对象右侧的情况。如果不是这种情况,则它们重叠。在这种情况下,您可以计算 重叠 的最小值和最大值,然后将其除以相关对象的宽度。

这是一个程序,其中根据您在问题中提供的坐标创建示例对象。您可以使用鼠标在对象周围拖动。绘制重叠并打印其值。

计算发生在 computeOverlap 方法中。

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

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


public class ShapeOverlap
{
    public static void main(String[] args) throws IOException
    {
        SwingUtilities.invokeLater(() -> createAndShowGUI());
    }

    private static void createAndShowGUI()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new ShapeOverlapPanel());
        f.setSize(900,500);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class ShapeOverlapPanel extends JPanel
    implements MouseListener, MouseMotionListener
{
    private final List<Point2D> points0;
    private final List<Point2D> points1;
    private final List<Point2D> draggedPoints;
    private Point previousMousePosition;

    ShapeOverlapPanel()
    {
        points0 = new ArrayList<Point2D>();
        points0.add(new Point2D.Double(160, 200));
        points0.add(new Point2D.Double(180, 200));
        points0.add(new Point2D.Double(270, 260));
        points0.add(new Point2D.Double(250, 260));

        points1 = new ArrayList<Point2D>();
        points1.add(new Point2D.Double(200, 280));
        points1.add(new Point2D.Double(220, 280));
        points1.add(new Point2D.Double(310, 340));
        points1.add(new Point2D.Double(290, 340));

        draggedPoints = new ArrayList<Point2D>();

        addMouseListener(this);
        addMouseMotionListener(this);
    }


    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,  
            RenderingHints.VALUE_ANTIALIAS_ON);
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, getWidth(), getHeight());

        Shape s0 = createShape(points0);        
        Shape s1 = createShape(points1);

        g.setColor(Color.RED);
        g.fill(s0);
        g.setColor(Color.BLUE);
        g.fill(s1);

        g.setColor(Color.GRAY);
        drawOverlap(g, s0, s1);

        double overlap = computeOverlap(s0, s1);
        g.drawString("Overlap of red from blue: "+overlap, 10, 20);
    }

    private static double computeOverlap(Shape s0, Shape s1)
    {
        Rectangle2D b0 = s0.getBounds2D();
        Rectangle2D b1 = s1.getBounds2D();

        if (b0.getMaxX() < b1.getMinX())
        {
            System.out.println("Shape 0 is left of shape 1");
            return Double.NaN;
        }
        if (b0.getMinX() > b1.getMaxX())
        {
            System.out.println("Shape 0 is right of shape 1");
            return Double.NaN;
        }

        double overlapMinX = Math.max(b0.getMinX(), b1.getMinX());
        double overlapMaxX = Math.min(b0.getMaxX(), b1.getMaxX());
        double overlapSize = overlapMaxX - overlapMinX;

        double relativeOverlap = overlapSize / b0.getWidth();
        return relativeOverlap;
    }

    private void drawOverlap(Graphics2D g, Shape s0, Shape s1)
    {
        Rectangle2D b0 = s0.getBounds2D();
        Rectangle2D b1 = s1.getBounds2D();

        if (b0.getMaxX() < b1.getMinX())
        {
            return;
        }
        if (b0.getMinX() > b1.getMaxX())
        {
            return;
        }

        double overlapMinX = Math.max(b0.getMinX(), b1.getMinX());
        double overlapMaxX = Math.min(b0.getMaxX(), b1.getMaxX());

        g.drawLine((int)overlapMinX, 0, (int)overlapMinX, getHeight());
        g.drawLine((int)overlapMaxX, 0, (int)overlapMaxX, getHeight());
    }


    private static Shape createShape(Iterable<? extends Point2D> points)
    {
        Path2D path = new Path2D.Double();
        boolean first = true;
        for (Point2D p : points)
        {
            if (first)
            {
                path.moveTo(p.getX(), p.getY());
                first = false;
            }
            else
            {
                path.lineTo(p.getX(), p.getY());
            }
        }
        path.closePath();
        return path;
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
        int dx = e.getX() - previousMousePosition.x;
        int dy = e.getY() - previousMousePosition.y;
        for (Point2D p : draggedPoints)
        {
            p.setLocation(p.getX() + dx, p.getY() + dy);
        }
        repaint();
        previousMousePosition = e.getPoint();
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
    }

    @Override
    public void mouseClicked(MouseEvent e)
    {
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        draggedPoints.clear();
        Shape s0 = createShape(points0);
        Shape s1 = createShape(points1);
        if (s0.contains(e.getPoint()))
        {
            draggedPoints.addAll(points0);
        }
        else if (s1.contains(e.getPoint()))
        {
            draggedPoints.addAll(points1);
        }
        previousMousePosition = e.getPoint();
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        draggedPoints.clear();
    }

    @Override
    public void mouseEntered(MouseEvent e)
    {
    }

    @Override
    public void mouseExited(MouseEvent e)
    {
    }


}