63

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?

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. But the code is rather long. Just curious to see what others come up with.

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
pete
  • 631
  • 1
  • 5
  • 3
  • 2
    You came up with the solution? Where is it? You can show it here and we can help you make it shorter and cleaner. – Milan Babuškov Feb 20 '10 at 19:05
  • 4
    Interresting question. I notice that your solution will only work if the rectangle is parallel with the axis. – Christian Madsen Feb 20 '10 at 19:09
  • Gman - yes in any order. Milan - this was asked of me during an interview so I don't have my code (I dont necessarily need to see code..an algorithm would be great too!). Christian - good point about it having to be parallel with the axis. – pete Feb 20 '10 at 19:11

11 Answers11

73
  • find the center of mass of corner points: 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
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: e.g. abs(a-b) < 1E-6)

Vinayak Garg
  • 6,518
  • 10
  • 53
  • 80
Curd
  • 12,169
  • 3
  • 35
  • 49
  • I would never have thought of it... after a quick mental check, I do believe it works. +1 – Platinum Azure Feb 20 '10 at 22:49
  • 4
    This is a clever solution. It basically finds the circumcircle of the "rectangle" and verifies that all four corners are lie on it. – rlbond Feb 20 '10 at 23:03
  • 9
    This is VERY inefficient. Use the dot product method provided by Vlad. Squareroots need hundrets of clock cycles. Also the dot product method is more numerically stable. – Axel Gneiting Feb 20 '10 at 23:33
  • 6
    @Axel & @Curd: Was the solution edited since it original posting? I don't see any square roots. I'm assuming `sqr(x) == x*x` which means `ddi` is actually the square of the distance from `cx` to `xi`. This should be pretty darn fast. – Brett Pontarelli Feb 21 '10 at 04:46
  • @Axel Gneiting and Brett Pontarelli: I started the answer with the two first lines and then immediately added the code. From the beginning the code only contained the calculation of the square of the distances, no square roots (hence the variable name dd, instead of d). If the distances have to be equal, of course the square of the distances have to be equal too. I didn't think that this had to be mentioned. – Curd Feb 21 '10 at 17:19
  • This is correct, we can prove this using the fact that the line joining the midpoint of a chord and centre of circle is perpendicular to the chord. This method also works for degenerate rectangles. –  Feb 21 '10 at 17:28
  • 5
    Okay, then I need to appologize. I thought sqr stands for squareroot. – Axel Gneiting Feb 21 '10 at 17:46
  • 4
    Some considerations concerning performance: this solution takes 20 additions/subtractions/divisions by constant 4 and 8 multiplications. It even could be optimized to drop the remaining square distance calculations if the first or second comparison failed. So the numbers above are worst case. Even this worst case is as good as the best case and 3 times better than the worst case of the solution by Vlad. – Curd Feb 21 '10 at 22:21
  • Concerning its speed: as it is tagged as an interview question, speed is probably secondary - and this is a clever solution. – Andras Vass Feb 23 '10 at 23:21
  • 1
    The **hard** part is to perform the comparison correctly. Simply testing for < 1e-16 is not enough (the `sqr` functions will ruin accucary). This requires careful analysis (and imho I doubt this question has any meaningful answer without extended precision arithmetic). – Alexandre C. Jul 22 '12 at 09:45
  • This does not seem to work for points (1,0),(-1,0),(0,1),(0,-1) – Wolfy Dec 22 '17 at 21:49
  • 1
    @Wolfy: Why do you think so?!? Center of mass is (0,0) and distance of center of mass to any of the 4 corners is 1.0 therefore test returns true. – Curd Dec 27 '17 at 10:30
  • If we have (1, 2), (1, 2), (10, 30), (10, 30), wont this algorithm return True? I believe the previously mentioned points do not form a valid rectangle. – Pedro Lopes May 28 '19 at 17:58
  • @Pedro Lopes: why such a complicated example? What about (0, 0), (0, 0), (0, 0), (0, 0)? Well, it depends on the definition of rectangle. If rectangles of area 0 should be included it's ok. If not an additional test for area == 0 must be done. – Curd May 28 '19 at 21:02
  • @Curd Good point, I was thinking about an axis an aligned rectangle for my example. – Pedro Lopes May 29 '19 at 09:12
  • you don't have to use square root, just make sure the differences in x, y are equal that is all |x_n-center_x| and |y_n-center_y| are equal –  Nov 29 '20 at 12:25
46
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);
}
Vlad
  • 35,022
  • 6
  • 77
  • 199
  • @Larry: your test is only for being a parallelogram – Vlad Feb 20 '10 at 19:15
  • @Larry: with checking diagonals it's correct now, but your test requires taking 6 square roots. – Vlad Feb 20 '10 at 19:22
  • 1
    @Timmy: in that case one needs to do a more complicated check: `if (IsOrthogonal(a, b, c)) return IsRectangle(a, b, c, d); else if (IsOrthogonal(a, b, d)) return IsRectangle(a, b, d, c); else return false;` – Vlad Feb 20 '10 at 19:28
  • I modified the answer accordingly – Vlad Feb 20 '10 at 19:32
  • what if a and b is a diagonal? – Timmy Feb 20 '10 at 19:36
  • 1
    IsOrthogonal( (10,9), (15,9), (15,6) ) = 0 or False. (15-10)*(15-15)+(9-9)*(9-6)=0. Is there a ! missing? – Harvey Feb 20 '10 at 19:47
  • @Vlad: Could you please explain the run-time complexity of your method in terms of arithmetic operations for the worst case just like @Curd did? – Andras Vass Feb 28 '10 at 00:08
  • Right triangles entered in order pass through this as rectangles. the isOrthogonal() method needs to be modified. – Sumi Nov 12 '20 at 17:54
7
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 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.

Bit Magic

  1. XORing equal value numbers return 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.

Vineet Kapoor
  • 8,249
  • 1
  • 11
  • 14
  • cool idea but possibly impractical if you need to test equality with a small tolerance to account for float precision. also probably worth adding that the xor trick works because xor is commutative and associative – Ed Bordin Apr 03 '19 at 00:23
  • 4.b. Why not just check if 4 distances are equal? – diegoide Aug 11 '19 at 01:37
6
  • 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.)

Andras Vass
  • 11,478
  • 1
  • 37
  • 49
5

The distance from one point to the other 3 should form a right triangle:

|   /      /|
|  /      / |
| /      /  |
|/___   /___|
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
Carlos Gutiérrez
  • 13,972
  • 5
  • 37
  • 47
4

If the points are A, B, C & D and you know the order then you calculate the vectors:

x=B-A, y=C-B, z=D-C and w=A-D

Then take the dot products (x dot y), (y dot z), (z dot w) and (w dot x). If they are all zero then you have a rectangle.

Axel Gneiting
  • 5,293
  • 25
  • 30
  • If you knew the order, then checking for |x| = |z|, |y| = |w| and one dot product would suffice. (Since opposite sides must be equal in length and then there are quite a few quadrilaterals with opposite sides equal in length.) – Andras Vass Feb 24 '10 at 21:14
2

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. Suppose we have lines 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. 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;
}
manugupt1
  • 2,337
  • 6
  • 31
  • 39
2

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.

If you have points [Ax,Ay] [Bx,By] [Cx,Cy] [Dx,Dy]

vector v = B-A vector u = C-A

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

so if (v.u == 0) there's a couple of perpendicular lines right there.

I actually don't know C programming, but here's some "meta" programming for you :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.

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}
David Meister
  • 3,941
  • 1
  • 26
  • 27
  • Ah, I just realised that this has that parallel-to-the axis problem. So instead of the if statements at the end it should test if (D == A + v + u). I also noticed that if you get the diagonal as one of your first 3 points it might give a false negative, so if the dot product fails it should redefine u as AD and try again. – David Meister Feb 22 '10 at 15:59
2

I recently faced a similar challenge, but in Python, this is what I came up with in Python, perhaps this method may be valuable. 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.

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
Mike M.
  • 21
  • 2
1

How about to verify those 4 points could form a parallelogram first, then finding out if there exists one right angle.
1. verify parallelogram

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

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.

bob wong
  • 21
  • 4
  • Please ensure that your answer is actually an answer to the OP's question. He asked how to find out if points form a rectangle, not if they form a parallelogram. Also, one right angle does not ensure a rectangle. – Jack Bashford Aug 05 '18 at 04:10
  • 1
    Ummm,one parallelogram with a right angle is a rectangle, so I believe it might work to verify it in the above two steps. – bob wong Aug 06 '18 at 01:20
  • My bad sorry, I was thinking a different way. – Jack Bashford Aug 06 '18 at 01:21
1

Here is my algorithm proposal, for an axis-aligned rectangle test, but in 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).

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)))
Pedro Lopes
  • 2,833
  • 1
  • 30
  • 36
  • This seems to assume *axis-aligned rectangle* (as the question suggests, but doesn't specify explicitly). (Why four points as args if iso-oriented?) – greybeard May 28 '19 at 19:53
  • @greybeard 4 points are provided as arguments, as asked for in the question. Yes, the assumption here is that it is checking for a axis aligned rectangle. I will update the the answer with that remark – Pedro Lopes May 29 '19 at 09:03