views:

1330

answers:

7

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.

+13  A: 
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
@Larry: your test is only for being a parallelogram
Vlad
@Larry: with checking diagonals it's correct now, but your test requires taking 6 square roots.
Vlad
points can be given in anyorder
Timmy
@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
I modified the answer accordingly
Vlad
what if a and b is a diagonal?
Timmy
IsOrthogonal( (10,9), (15,9), (15,6) ) = 0 or False. (15-10)*(15-15)+(9-9)*(9-6)=0. Is there a ! missing?
Harvey
2Timmy: indeed, one more correction, thanks!
Vlad
2Harvey: indeed! :)
Vlad
@downvoter: reason?
Vlad
@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
+23  A: 
  • find the center of mass of corner points: cx=(x1+x2+x3+x4)/4, cy=(y1+y2+y3+y4)/4
  • test if 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)

Curd
I would never have thought of it... after a quick mental check, I do believe it works. +1
Platinum Azure
This is a clever solution. It basically finds the circumcircle of the "rectangle" and verifies that all four corners are lie on it.
rlbond
Also, the points don't have to be in order.
rlbond
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
Brett Pontarelli
@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
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.
Moron
Okay, then I need to appologize. I thought sqr stands for squareroot.
Axel Gneiting
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
Concerning its speed: as it is tagged as an interview question, speed is probably secondary - and this is a clever solution.
andras
+1  A: 

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
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
A: 

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
I think this is computationally the most efficient.
milesmeow
Division by zero?
Larry
You should check for m4 as well, else you may end up with a trapezoid.
andras
A: 

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
Hmmm. Will this work with a parallelogram?
andras
@andras - Tested a few parallelograms and all evaluated as false. Are you thinking about a particular case?
Carlos Gutiérrez
Suppose we have points x1=3, y1=3; x2=0, y2=0; x3=6, y3=0; x4=9, y4=3;Now d1 = 18; d2 = 18; d3 = 36; It's getting late, though. :-)Would you please check?
andras
@andras - You're right, it looks like the test must be repeated using 3 of the points as start point.
Carlos Gutiérrez
@Carlos: please, do something about it then.
andras
A: 

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
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
A: 
  • 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
Worst case: 15 additions/subtractions, 6 multiplications.
andras