简体   繁体   English

求平面上的 4 个点是否构成一个矩形?

[英]find if 4 points on a plane form a rectangle?

Can somebody please show me in C-style pseudocode how to write a function (represent the points however you like) that returns true if 4-points (args to the function) form a rectangle, and false otherwise?有人可以用 C 风格的伪代码告诉我如何编写一个函数(代表你喜欢的点),如果 4 个点(函数的参数)形成一个矩形,则返回 true,否则返回 false?

I came up with a solution that first tries to find 2 distinct pairs of points with equal x-value, then does this for the y-axis.我想出了一个解决方案,首先尝试找到具有相等 x 值的 2 对不同的点,然后对 y 轴执行此操作。 But the code is rather long.但是代码比较长。 Just curious to see what others come up with.只是好奇看看其他人想出了什么。

  • find the center of mass of corner points: cx=(x1+x2+x3+x4)/4, cy=(y1+y2+y3+y4)/4求角点的质心:cx=(x1+x2+x3+x4)/4, cy=(y1+y2+y3+y4)/4
  • test if square of distances from center of mass to all 4 corners are equal测试从质心到所有 4 个角的距离的平方是否相等
bool isRectangle(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { double cx,cy; double dd1,dd2,dd3,dd4; cx=(x1+x2+x3+x4)/4; cy=(y1+y2+y3+y4)/4; dd1=sqr(cx-x1)+sqr(cy-y1); dd2=sqr(cx-x2)+sqr(cy-y2); dd3=sqr(cx-x3)+sqr(cy-y3); dd4=sqr(cx-x4)+sqr(cy-y4); return dd1==dd2 && dd1==dd3 && dd1==dd4; }

(Of course in practice testing for equality of two floating point numbers a and b should be done with finite accuracy: eg abs(ab) < 1E-6) (当然,在实践中测试两个浮点数 a 和 b 的相等性应该以有限的精度完成:例如 abs(ab) < 1E-6)

struct point
{
    int x, y;
}

// tests if angle abc is a right angle
int IsOrthogonal(point a, point b, point c)
{
    return (b.x - a.x) * (b.x - c.x) + (b.y - a.y) * (b.y - c.y) == 0;
}

int IsRectangle(point a, point b, point c, point d)
{
    return
        IsOrthogonal(a, b, c) &&
        IsOrthogonal(b, c, d) &&
        IsOrthogonal(c, d, a);
}

If the order is not known in advance, we need a slightly more complicated check:如果订单事先不知道,我们需要稍微复杂一点的检查:

int IsRectangleAnyOrder(point a, point b, point c, point d)
{
    return IsRectangle(a, b, c, d) ||
           IsRectangle(b, c, a, d) ||
           IsRectangle(c, a, b, d);
}
  • translate the quadrilateral so that one of its vertices now lies at the origin平移四边形,使其顶点之一现在位于原点
  • the three remaining points form three vectors from the origin剩下的三个点从原点形成三个向量
  • one of them must represent the diagonal其中之一必须代表对角线
  • the other two must represent the sides另外两个必须代表双方
  • by the parallelogram rule if the sides form the diagonal, we have a parallelogram根据平行四边形规则,如果边形成对角线,我们有一个平行四边形
  • if the sides form a right angle, it is a parallelogram with a right angle如果边成直角,它是一个有直角的平行四边形
  • opposite angles of a parallelogram are equal平行四边形的对角相等
  • consecutive angles of a parallelogram are supplementary平行四边形的连续角是互补的
  • therefore all angles are right angles所以所有的角都是直角
  • it is a rectangle它是一个矩形
  • it is much more concise in code, though :-)不过,它在代码中更加简洁:-)

     static bool IsRectangle( int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { x2 -= x1; x3 -= x1; x4 -= x1; y2 -= y1; y3 -= y1; y4 -= y1; return (x2 + x3 == x4 && y2 + y3 == y4 && x2 * x3 == -y2 * y3) || (x2 + x4 == x3 && y2 + y4 == y3 && x2 * x4 == -y2 * y4) || (x3 + x4 == x2 && y3 + y4 == y2 && x3 * x4 == -y3 * y4); }
  • (If you want to make it work with floating point values, please, do not just blindly replace the int declarations in the headers. It is bad practice. They are there for a reason. One should always work with some upper bound on the error when comparing floating point results.) (如果你想让它使用浮点值,请不要盲目地替换标头中的int声明。这是不好的做法。它们存在是有原因的。人们应该始终使用错误的一些上限比较浮点结果时。)

1. Find all possible distances between given 4 points. (we will have 6 distances)
2. XOR all distances found in step #1
3. If the result after XORing is 0 then given 4 points are definitely vertices of a square or a rectangle otherwise, return false (given 4 points do not form a rectangle).
4. Now, to differentiate between square and rectangle 
   a. Find the largest distance out of 4 distances found in step #1. 
   b. Check if the largest distance / Math.sqrt (2) is equal to any other distance.
   c. If answer is No, then given four points form a rectangle otherwise they form a square.

Here, we are using geometric properties of rectangle/square and Bit Magic .在这里,我们使用了rectangle/squareBit Magic 的几何属性。

Rectangle properties in play正在播放的矩形属性

  1. Opposite sides and diagonals of a rectangle are of equal length.长方形的对边和对角线的长度相等。
  2. If the diagonal length of a rectangle is sqrt(2) times any of its length, then the rectangle is a square.如果矩形的对角线长度是 sqrt(2) 乘以它的任何长度,则该矩形是正方形。

Bit Magic位魔法

  1. XORing equal value numbers return 0.对等值数进行异或运算返回 0。

Since distances between 4 corners of a rectangle will always form 3 pairs, one for diagonal and two for each side of different length, XORing all the values will return 0 for a rectangle.由于矩形的 4 个角之间的距离将始终形成 3 对,一对用于对角线,每对不同长度的边各两对,因此对矩形的所有值进行异或运算将返回 0。

If the points are A, B, C & D and you know the order then you calculate the vectors:如果点是 A、B、C 和 D,并且您知道顺序,那么您可以计算向量:

x=BA, y=CB, z=DC and w=AD x=BA, y=CB, z=DC 和 w=AD

Then take the dot products (x dot y), (y dot z), (z dot w) and (w dot x).然后取点积 (x dot y)、(y dot z)、(z dot w) 和 (w dot x)。 If they are all zero then you have a rectangle.如果它们都为零,那么你有一个矩形。

The distance from one point to the other 3 should form a right triangle:从一个点到另一个 3 点的距离应形成一个直角三角形:

|   /      /|
|  /      / |
| /      /  |
|/___   /___|
d1 = sqrt( (x2-x1)^2 + (y2-y1)^2 ) 
d2 = sqrt( (x3-x1)^2 + (y3-y1)^2 ) 
d3 = sqrt( (x4-x1)^2 + (y4-y1)^2 ) 
if d1^2 == d2^2 + d3^2 then it's a rectangle

Simplifying:简化:

d1 = (x2-x1)^2 + (y2-y1)^2
d2 = (x3-x1)^2 + (y3-y1)^2
d3 = (x4-x1)^2 + (y4-y1)^2
if d1 == d2+d3 or d2 == d1+d3 or d3 == d1+d2 then return true

We know that two staright lines are perpendicular if product of their slopes is -1,since a plane is given we can find the slopes of three consecutive lines and then multiply them to check if they are really perpendicular or not.我们知道如果两条直线的斜率乘积为-1,那么两条直线是垂直的,因为给定了一个平面,我们可以找到三个连续直线的斜率,然后将它们相乘以检查它们是否真的垂直。 Suppose we have lines L1,L2,L3.假设我们有线 L1、L2、L3。 Now if L1 is perpendicular to L2 and L2 perpendicular to L3, then it is a rectangle and slope of the m(L1)*m(L2)=-1 and m(L2)*m(L3)=-1, then it implies it is a rectangle.现在如果 L1 垂直于 L2,L2 垂直于 L3,那么它是一个矩形,斜率 m(L1)*m(L2)=-1 和 m(L2)*m(L3)=-1,那么它暗示它是一个矩形。 The code is as follows代码如下

bool isRectangle(double x1,double y1,
        double x2,double y2,
        double x3,double y3,
        double x4,double y4){
    double m1,m2,m3;
    m1 = (y2-y1)/(x2-x1);
    m2 = (y2-y3)/(x2-x3);
    m3 = (y4-y3)/(x4-x3);

    if((m1*m2)==-1 && (m2*m3)==-1)
        return true;
    else
        return false;
}

taking the dot product suggestion a step further, check if two of the vectors made by any 3 of the points of the points are perpendicular and then see if the x and y match the fourth point.将点积建议更进一步,检查点的任意 3 个点构成的两个向量是否垂直,然后查看 x 和 y 是否与第四个点匹配。

If you have points [Ax,Ay] [Bx,By] [Cx,Cy] [Dx,Dy]如果你有点 [Ax,Ay] [Bx,By] [Cx,Cy] [Dx,Dy]

vector v = BA vector u = CA向量 v = BA 向量 u = CA

v(dot)u/|v||u| v(点)u/|v||u| == cos(theta) == cos(θ)

so if (vu == 0) there's a couple of perpendicular lines right there.所以如果 (vu == 0) 那里有几条垂直线。

I actually don't know C programming, but here's some "meta" programming for you :P我实际上不知道 C 编程,但这里有一些“元”编程给你:P

if (v==[0,0] || u==[0,0] || u==v || D==A) {not a rectangle, not even a quadrilateral}

var dot = (v1*u1 + v2*u2); //computes the "top half" of (v.u/|v||u|)
if (dot == 0) { //potentially a rectangle if true

    if (Dy==By && Dx==Cx){
     is a rectangle
    }

    else if (Dx==Bx && Dy==Cy){
     is a rectangle
    }
}
else {not a rectangle}

there's no square roots in this, and no potential for a divide by zero.这没有平方根,也没有被零除的可能性。 I noticed people mentioning these issues on earlier posts so I thought I'd offer an alternative.我注意到人们在之前的帖子中提到了这些问题,所以我想我会提供一个替代方案。

So, computationally, you need four subtractions to get v and u, two multiplications, one addition and you have to check somewhere between 1 and 7 equalities.因此,在计算上,您需要四次减法才能得到 v 和 u、两次乘法、一次加法,并且您必须检查 1 到 7 个等式之间的某个位置。

maybe I'm making this up, but i vaguely remember reading somewhere that subtractions and multiplications are "faster" calculations.也许我是在编造这个,但我依稀记得在某处读到减法和乘法是“更快”的计算。 I assume that declaring variables/arrays and setting their values is also quite fast?我认为声明变量/数组并设置它们的值也很快?

Sorry, I'm quite new to this kind of thing, so I'd love some feedback to what I just wrote.抱歉,我对这种事情很陌生,所以我很想对我刚刚写的内容提供一些反馈。

Edit: try this based on my comment below:编辑:根据我下面的评论试试这个:

A = [a1,a2];
B = [b1,b2];
C = [c1,c2];
D = [d1,d2];

u = (b1-a1,b2-a2);
v = (c1-a1,c2-a2);

if ( u==0 || v==0 || A==D || u==v)
    {!rectangle} // get the obvious out of the way

var dot = u1*v1 + u2*v2;
var pgram = [a1+u1+v1,a2+u2+v2]
if (dot == 0 && pgram == D) {rectangle} // will be true 50% of the time if rectangle
else if (pgram == D) {
    w = [d1-a1,d2-a2];

    if (w1*u1 + w2*u2 == 0) {rectangle} //25% chance
    else if (w1*v1 + w2*v2 == 0) {rectangle} //25% chance

    else {!rectangle}
}
else {!rectangle}

I recently faced a similar challenge, but in Python, this is what I came up with in Python, perhaps this method may be valuable.我最近遇到了类似的挑战,但是在 Python 中,这是我在 Python 中提出的,也许这种方法可能很有价值。 The idea is that there are six lines, and if created into a set, there should be 3 unique line distances remaining - the length, width, and diagonal.这个想法是有 6 条线,如果创建成一个集合,应该剩下 3 个唯一的线距 - 长度、宽度和对角线。

def con_rec(a,b,c,d): 
        d1 = a.distanceFromPoint(b)
        d2 = b.distanceFromPoint(c)
        d3 = c.distanceFromPoint(d)
        d4 = d.distanceFromPoint(a)
        d5 = d.distanceFromPoint(b)
        d6 = a.distanceFromPoint(c)
        lst = [d1,d2,d3,d4,d5,d6] # list of all combinations 
        of point to point distances
        if min(lst) * math.sqrt(2) == max(lst): # this confirms a square, not a rectangle
            return False
        z = set(lst) # set of unique values in ck
        if len(lst) == 3: # there should be three values, length, width, diagonal, if a 
        4th, it's not a rectangle
            return True
        else: # not a rectangle
            return False

How about to verify those 4 points could form a parallelogram first, then finding out if there exists one right angle .先验证这4个点能不能形成平行四边形,再看是否存在一个直角
1. verify parallelogram 1.验证平行四边形

input 4 points A, B, C, D;

if(A, B, C, D are the same points), exit;// not a rectangle;

else form 3 vectors, AB, AC, AD, verify(AB=AC+AD || AC=AB+AD || AD=AB+AC), \\\\if one of them satisfied, this is a parallelogram;

2. verify a right angle 2.验证一个直角

through the last step, we could find which two points are the adjacent points of A;

We need to find out if angle A is a right angle, if it is, then rectangle.

I did not know if there exist bugs.我不知道是否存在错误。 Please figure it out if there is.请问有没有。

Here is my algorithm proposal, for an axis-aligned rectangle test, but in Python.这是我的算法建议,用于轴对齐矩形测试,但在 Python 中。

The idea is to grab the first point as a pivot, and that all the other points must conform to the same width and height, and checks that all points are distinct, via a set, to account for cases such as (1, 2), (1, 2), (10, 30), (10, 30).这个想法是抓住第一个点作为枢轴,所有其他点必须符合相同的宽度和高度,并通过一组检查所有点是否不同,以解决诸如 (1, 2) 之类的情况, (1, 2), (10, 30), (10, 30)。

from collections import namedtuple

Point = namedtuple('Point', ('x', 'y'))

def is_rectangle(p1, p2, p3, p4) -> bool:
    width = None
    height = None

    # All must be distinct
    if (len(set((p1, p2, p3, p4))) < 4):
        return False

    pivot = p1

    for point in (p2, p3, p4):
        candidate_width = point.x - pivot.x
        candidate_height = point.y - pivot.y

        if (candidate_width != 0):
            if (width is None):
                width = candidate_width
            elif (width != candidate_width):
                return False

        if (candidate_height != 0):
            if (height is None):
                height = candidate_height
            elif (height != candidate_height):
                return False

    return width is not None and height is not None

# Some Examples
print(is_rectangle(Point(10, 50), Point(20, 50), Point(10, 40), Point(20, 40)))
print(is_rectangle(Point(100, 50), Point(20, 50), Point(10, 40), Point(20, 40)))
print(is_rectangle(Point(10, 10), Point(20, 50), Point(10, 40), Point(20, 40)))
print(is_rectangle(Point(10, 30), Point(20, 30), Point(10, 30), Point(20, 30)))
print(is_rectangle(Point(10, 30), Point(10, 30), Point(10, 30), Point(10, 30)))
print(is_rectangle(Point(1, 2), Point(10, 30), Point(1, 2), Point(10, 30)))
print(is_rectangle(Point(10, 50), Point(80, 50), Point(10, 40), Point(80, 40)))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM