从2个矩形中获取交点

Posted

技术标签:

【中文标题】从2个矩形中获取交点【英文标题】:Get the points of intersection from 2 rectangles 【发布时间】:2013-11-14 05:03:11 【问题描述】:

假设我们有两个矩形,分别定义了它们的左下角和右上角。例如:rect1 (x1, y1)(x2, y2)rect2 (x3, y3)(x4, y4)。 我正在尝试找到相交矩形的坐标(左下角和右上角)。

任何想法、算法、伪代码,将不胜感激。

附言我发现了类似的问题,但他们只检查 2 个矩形是否相交。

【问题讨论】:

A) 这很容易搜索,我保证互联网已经有了答案 B) 如果我们正在谈论轴对齐的矩形(我们似乎是这样),您应该能够重新发明***太快了。你试过了吗? 我在网上搜索过,但找不到任何有用的信息——大多数解决方案只检查是否存在交叉点,不返回新矩形的坐标。你能解释一下关于轴对齐的矩形吗,因为我不明白。 p.s.对不起我的英语不好 @Dave:出于某种无法解释的原因,尝试为两个矩形相交问题“重新发明***”产生的解决方案比所需的“重”大约 4 到 8 倍。 @NoSense scheme 标签是留给关于Scheme编程语言的问题的,这个问题不是关于它的,你为什么一直加标签? Java method to find the rectangle that is the intersection of two rectangles using only left bottom point, width and height?的可能重复 【参考方案1】:

如果输入矩形是标准化的,即您已经知道x1 < x2y1 < y2(第二个矩形也是如此),那么您需要做的就是计算

int x5 = max(x1, x3);
int y5 = max(y1, y3);
int x6 = min(x2, x4);
int y6 = min(y2, y4);

它会给你你的交集为矩形(x5, y5)-(x6, y6)。如果原始矩形不相交,则结果将是一个“退化”矩形(带有x5 >= x6 和/或y5 >= y6),您可以轻松检查。

附:像往常一样,小细节将取决于您是否必须将接触矩形视为相交。

【讨论】:

这也可以扩展到更高的维度。假设一个矩形有一个 U 和 L 向量,其中包含所有维度的上限和下限。 (第一个矩形的 U 是 [x2, y2];L 是 [x1, y1]。但它们可能是 [x, y, z, w, ...] 坐标。)然后 intersection.L = pointwise_maximum(rect1. L, rect2.L);交叉点.U = pointwise_minimum(rect1.U, rect2.U)。那么如果有的话(intersection.U - intersection.L 【参考方案2】:

要查找交叉点,您必须对点进行一些简单的比较:

所以我们可以从图像中看到如果 x3, y3 大于或等于 x1, y1 并且小于或等于 x2, y2 那么它在第一个矩形内,同样你需要检查 x4, y4也在 x1,y1 到 x2,y2 的范围内。

如果这两个条件都成立,那么您可以确定第二个矩形完全被第一个包围。

如果找出里面哪个对你很重要,你也需要反过来检查。

您还必须让矩形轴对齐,否则这将无法可靠地工作。

如果您需要更多详细信息,请告诉我,虽然我认为快速的 Google 搜索会很容易为您发现更多详细信息,但请告诉我,如果您愿意,我可以制作一个矩形碰撞教程。

更详细:

要确定矩形是否有任何交点,您可以检查它们定义点的坐标,出于我们的目的,我们将使用左上角和右下角坐标。 我们可以利用一个类来使这对我们更容易,并最大限度地提高代码的可用性,我们可以使用 2d Vector 和 2d Point: 2dVectorPoint.h

#include <cmath>

class Vector2D

    public:
        float   x;
        float   y;

        Vector2D()     
        Vector2D(float inX, float inY)
        
            x = inX;
            y = inY;
        

        Vector2D& Set(float inX, float inY)
        
            x = inX;
            y = inY;
            return (*this);
        

        float& operator [](long k)                    return ((&x)[k]);        

        const float& operator [](long k) const                    return ((&x)[k]);        

        Vector2D& operator +=(const Vector2D& v)
        
            x += v.x;
            y += v.y;
            return (*this);
        

        Vector2D& operator -=(const Vector2D& v)
        
            x -= v.x;
            y -= v.y;
            return (*this);
        

        Vector2D& operator *=(float t)
        
            x *= t;
            y *= t;
            return (*this);
        

        Vector2D& operator /=(float t)
        
            float f = 1.0F / t;
            x *= f;
            y *= f;
            return (*this);
        

        Vector2D& operator &=(const Vector2D& v)
        
            x *= v.x;
            y *= v.y;
            return (*this);
        

        Vector2D operator -(void) const                    return (Vector2D(-x, -y));        

        Vector2D operator +(const Vector2D& v) const                    return (Vector2D(x + v.x, y + v.y));        

        Vector2D operator -(const Vector2D& v) const          return (Vector2D(x - v.x, y - v.y));        

        Vector2D operator *(float t) const                    return (Vector2D(x * t, y * t));        

        Vector2D operator /(float t) const             float f = 1.0F / t; return (Vector2D(x * , y * f));        

        float operator *(const Vector2D& v) const                    return (x * v.x + y * v.y);        

        Vector2D operator &(const Vector2D& v) const                 return (Vector2D(x * v.x, y * v.y));        

        bool operator ==(const Vector2D& v) const                    return ((x == v.x) && (y == v.y));        

        bool operator !=(const Vector2D& v) const                    return ((x != v.x) || (y != v.y));        

        Vector2D& Normalize(void)                                    return (*this /= sqrtf(x * x + y * y));        

        Vector2D& Rotate(float angle);
;


class Point2D : public Vector2D

    public:

        Point2D() 

        Point2D(float r, float s) : Vector2D(r, s) 

        Point2D& operator =(const Vector2D& v)
        
            x = v.x;
            y = v.y;
            return (*this);
        

        Point2D& operator *=(float t)
        
            x *= t;
            y *= t;
            return (*this);
        

        Point2D& operator /=(float t)
        
            float f = 1.0F / t;
            x *= f;
            y *= f;
            return (*this);
        

        Point2D operator -(void) const            return (Point2D(-x, -y));        

        Point2D operator +(const Vector2D& v) const                    return (Point2D(x + v.x, y + v.y));        

        Point2D operator -(const Vector2D& v) const                    return (Point2D(x - v.x, y - v.y));        

        Vector2D operator -(const Point2D& p) const                    return (Vector2D(x - p.x, y - p.y));        

        Point2D operator *(float t) const                    return (Point2D(x * t, y * t));        

        Point2D operator /(float t) const
        
            float f = 1.0F / t;
            return (Point2D(x * f, y * f));
        
;


inline Vector2D operator *(float t, const Vector2D& v)    return (Vector2D(t * v.x, t * v.y));

inline Point2D operator *(float t, const Point2D& p)    return (Point2D(t * p.x, t * p.y));

inline float Dot(const Vector2D& v1, const Vector2D& v2)    return (v1 * v2);

inline float Magnitude(const Vector2D& v)    return (sqrtf(v.x * v.x + v.y * v.y));

inline float InverseMag(const Vector2D& v)    return (1.0F / sqrtf(v.x * v.x + v.y * v.y));

inline float SquaredMag(const Vector2D& v)    return (v.x * v.x + v.y * v.y);

struct Origin2D_

    const Point2D& operator +(const Vector2D& v)            return (static_cast<const Point2D&>(v));    

    Point2D operator -(const Vector2D& v)            return (Point2D(-v.x, -v.y));    
;

2dVectorPoint.cpp

#include "2dVectorPoint.h"

Origin2D_ Origin2D;

Vector2D& Vector2D::Rotate(float angle)

    float s = sinf(angle);
    float c = cosf(angle);

    float nx = c * x - s * y;
    float ny = s * x + c * y;

    x = nx;
    y = ny;

    return (*this);

extern Origin2D_ Origin2D;

使用的代码改编自 here 以节省我的手指。

然后我们可以利用它来轻松比较: 我们可以将矩形 1 定义为以 P1 和 P2 作为其边界,将矩形 2 定义为以 P3 和 P4 作为其边界,给我们以下比较:

if ( P2.y <= P3.y && P1.y >= P4.y && P2.x>= P3.x && P1.x <= P4.x )

    return true;

这将为任何相交实例或矩形 1 完全包含矩形 2 返回一个真值。

要仅检查交叉点,只需删除相等检查(将所有= 从上述等式中取出),您将只检查交叉点。如果你有一个交点,你可以使用线性代数来评估确切的坐标。

【讨论】:

非常感谢。它帮助我了解我实际上在做什么。我会标记 AndreyT 的答案,因为它对我的案例工作来说既简单又特别,就像一个魅力:) 没问题@NoSense 很高兴为您提供帮助。这是我能要求的最好的,为了帮助理解,它比在没有任何理解的情况下提供解决方案更有价值。希望以后再见!【参考方案3】:

假设一个盒子有一个半径 X 和半径 Y(我知道它没有,但这个术语在这里很有用)。

你将拥有:

rect1_x_radius = (x2-x1)/2
rect1_y_radius = (y2-y1)/2

rect2_x_radius = (x4-x3)/2
rect2_y_radius = (y4-y3)/2

现在,如果矩形中点在适当方向上的距离比它们的半径总和更远 - 它们不会发生碰撞。 否则他们会这样做 - 这个提示就足够了。

你现在应该可以完成你的任务了。

更新:

好的 - 让我们解决一维问题 - 稍后您将解决它的二维问题。看看这件......艺术;-)

您会看到 2 个片段 - 现在进行一些计算:

rA = (maxA-minA) / 2
rB = (maxB-minB) / 2

midA = minA + rA
midB = minB + rB

mid_dist = |midA - midB|

现在如何检查是否发生碰撞?正如我所说,如果“半径”的总和小于线段的距离 - 没有碰撞:

if ( mid_dist > fabs(rA+rB) )

    // no intersection

else

    // segments intersect

现在您的工作是计算 1D 和 2D 中的交集/公共部分。现在由您决定(或者您可以阅读 Andrey 的答案)。

这是相同的情况,但在 2D 中 - 两种 1D 情况:

【讨论】:

我没听懂。我需要获取交叉点,而不是检查是否有任何交叉点... @NoSense:使用更简单的示例 - 首先在 1D 中解决您的问题 - 想象 X 轴上的 2 个向量 - 计算它们的“半径”((end-start)/2) 和两个向量的中点。如果中间点之间的距离大于半径之和,则它们不会发生碰撞。好的?让我知道是否清楚 - 我不想为您提供完整的解决方案。 我还是无法理解你的想法。我知道如何检查是否有交叉路口;我想知道如何获得积分... 对不起 :(,也许我很愚蠢,但不明白如何使用它...无论如何感谢您的帮助 @NoSense:看图片 - 现在 (rA+rB) 【参考方案4】:

xy方向可以分别处理。

假设x1 &lt;= x3(第一个框至少和第二个一样在左边)。那么,重叠当且仅当x1 &lt;= x3 &lt;= x2

同样,假设y1 &lt;= y3(第一个框至少与第二个框一样远)。那么,重叠当且仅当y1 &lt;= y3 &lt;= y2

如果两个方向都有重叠,则有一个矩形重叠。您可以通过将xy 坐标排序并选择中间两个来找到坐标。

在伪代码中:

if (((x1 <= x3 && x3 <= x2) || (x3 <= x1 && x1 <= x4)) // x-overlap
    &&
    ((y1 <= y3 && y3 <= y2) || (y3 <= y1 && y1 <= y4)) // y-overlap
) 
    int[] xs = x1, x2, x3, x4;
    int[] ys = y1, y2, y3, y4;
    sort(xs);
    sort(ys);

    // bottom-left: xs[1], ys[1]
    // top-right:   xs[2], ys[2]

【讨论】:

好的,谢谢,这也许是一个解决方案,但我必须检查它是否总是有效。 @AndreyT 我不太确定我是否明白你的意思。如果x1 &lt;= x3x2 &gt; x4,矩形可能仍然相交,对吧? @Heuster:不好意思,我把变量搞混了。如果初始矩形被归一化,则条件过度。为了检查两个矩形是否相交,只需要x1 &lt; x4 &amp;&amp; x3 &lt; x2 &amp;&amp; y1 &lt; y4 &amp;&amp; y3 &lt; y2。而已。 (我使用严格的不等式来排除接触的矩形。)【参考方案5】:

以防万一简单的 C# 解决方案适合任何人:

public struct Rectangle

    public double Left  get; 
    public double Top  get; 
    public double Width  get; 
    public double Height  get; 
    public double Right => Left + Width;
    public double Bottom => Top + Height;
    public static Rectangle Empty  get;  = new Rectangle(0, 0, 0, 0);

    public Rectangle(double left, double top, double width, double height)
    
        Left = left;
        Top = top;
        Width = width;
        Height = height;
    

    public static bool RectanglesIntersect(Rectangle rectangle1, Rectangle rectangle2)
    
        rectangle1 = rectangle1.Normalize();
        rectangle2 = rectangle2.Normalize();

        if (rectangle2.Left >= rectangle1.Right)
            return false;
        if (rectangle2.Right <= rectangle1.Left)
            return false;

        if (rectangle2.Top >= rectangle1.Bottom)
            return false;
        if (rectangle2.Bottom <= rectangle1.Top)
            return false;

        return true;
    

    public static Rectangle GetIntersection(Rectangle rectangle1, Rectangle rectangle2)
    
        rectangle1 = rectangle1.Normalize();
        rectangle2 = rectangle2.Normalize();

        if (rectangle1.IntersectsWith(rectangle2))
        
            double left = Math.Max(rectangle1.Left, rectangle2.Left);
            double width = Math.Min(rectangle1.Right, rectangle2.Right) - left;
            double top = Math.Max(rectangle1.Top, rectangle2.Top);
            double height = Math.Min(rectangle1.Bottom, rectangle2.Bottom) - top;

            return new Rectangle(left, top, width, height);
        

        return Empty;
    

    public Rectangle GetIntersection(Rectangle rectangle)
    
        return GetIntersection(this, rectangle);
    

    public bool IntersectsWith(Rectangle rectangle)
    
        return RectanglesIntersect(this, rectangle);
    

    public Rectangle NormalizeWidth()
    
        if (Width >= 0)
            return this;
        Rectangle result = new Rectangle(Left + Width, Top, -Width, Height);
        return result;
    

    public Rectangle NormalizeHeight()
    
        if (Height >= 0)
            return this;
        Rectangle result = new Rectangle(Left, Top + Height, Width, -Height);
        return result;
    

    public Rectangle Normalize()
    
        Rectangle result = NormalizeWidth().NormalizeHeight();
        return result;
    

【讨论】:

以上是关于从2个矩形中获取交点的主要内容,如果未能解决你的问题,请参考以下文章

多个矩形,求覆盖面积,周长,及交点

使用霍夫变换进行矩形检测

如何找到直线和矩形的交点?

数据结构与算法之深入解析“最小矩形面积”的求解思路与算法示例

UVA - 10382 Watering Grass(几何)

POJ 1410 判断线段与矩形交点或在矩形内