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.
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)
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);
}
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
Opposite sides and diagonals of a rectangle are of equal length.
If the diagonal length of a rectangle is sqrt(2) times any of its length, then the rectangle is a square.
Bit Magic
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.
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.)
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
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.
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;
}
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}
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
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.
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)))
Related
I'm trying to implement an atan2-like function to map two input sinusoidal signals of arbitrary relative phase shift to a single output signal that linearly goes from 0 to 2π. atan2 normally assumes two signals with a 90 deg phase shift.
Given y0(x) = sin(x) and y1 = sin(x + phase), where phase is a fixed non-zero value, how can I implement a way to return x modulo 2π?
atan2 returns the angle of a 2d vector. Your code does not handle such scaling properly. But no worries, it's actually very easy to reduce your problem to an atan2 that would handle everything nicely.
Notice that calculating sin(x) and sin(x + phase) is the same as projecting a point (cos(x), sin(x)) onto the axes (0, 1) and (sin(phase), cos(phase)). This is the same as taking dot products with those axes, or transforming the coordinate system from the standard orthogonal basis into the skewed one. This suggests a simple solution: inverse the transformation to get the coordinates in the orthogonal basis and then use atan2.
Here's a code that does that:
double super_atan2(double x0, double x1, double a0, double a1) {
double det = sin(a0 - a1);
double u = (x1*sin(a0) - x0*sin(a1))/det;
double v = (x0*cos(a1) - x1*cos(a0))/det;
return atan2(v, u);
}
double duper_atan2(double y0, double y1, double phase) {
const double tau = 6.28318530717958647692; // https://tauday.com/
return super_atan2(y0, y1, tau/4, tau/4 - phase);
}
super_atan2 gets the angles of the two projection axes, duper_atan2 solves the problem exactly as you stated.
Also notice that the calculation of det is not strictly necessary. It is possible to replace it by fmod and copysign (we still need the correct sign of u and v).
Derivation:
In code:
// assume phase != k * pi, for any integer k
double f (double y0, double y1, double phase)
{
double u = (- y0 * cos(phase) + y1) / sin(phase);
double v = y0;
double x = atan2 (v, u);
return (x < 0) ? (x + 2 * M_PI) : x;
}
Let's say I have 2 points in 3D space, one at:
x=2, y=3, z=5
and the second one at:
x=6, y=7, z=10
What is the fastest way, in code, to calculate the coordinates of a third point from extending (for example, doubling) the distance between those two points (relative to point one)?
If you want a point extended as far beyond (x2,y2,z2) as that is beyond (x1,y1,z1):
x3 = x2 + (x2 - x1) (= 10)
y3 = y2 + (y2 - y1) (= 11)
z3 = z2 + (z2 - z1) (= 15)
or:
(x2 * 2 - x1, y2 * 2 -y1, z2 * 2 - z1)
Simple as that.
If you want something other than double the length, you can scale the (x2 - x1)-type terms. For example, if you want it 50% longer than the current line, multiply them by 0.5 (+50%). If you want it three times longer, multiply them by two (+200%).
In terms of code that can perform this extension, something like this, which gives you an endpoint pDest that, along with, p1 forms a line percent times the size of p1-p2:
typedef struct {
double x;
double y;
double z;
} tPoint3d;
void extend (tPoint3d *p1, tPoint3d *p2, double percent, tPoint3d *pDest) {
percent -= 100.0; // what to ADD
percent /= 100.0; // make multiplier
pDest->x = p2->x + percent * (p2->x - p1->x); // scale each point
pDest->y = p2->y + percent * (p2->y - p1->y);
pDest->z = p2->z + percent * (p2->z - p1->z);
}
In Matlab, I have a 3D-curve (array with 3 coordinates) that crosses the xy-plane in a 3 dimensional coordinate system. The "curve" is just a bunch of x,y,z points and its shape is elliptical that spans across the 3D space and spirals towards the origin. It crosses the xy plane several times and I would like to interpolate the x and y coordinates when the curve intersects the xy plane (i.e. when z=0). How do I do so?
Assuming you have 3 vectors x, y, and z with coordinates,
crossidx = find(diff(sign(z)) ~= 0); % z(zcross) and z(zcross+1) have different signs
z1 = z(crossidx);
z2 = z(crossidx+1);
dz = z2 - z1;
alpha = -z1;
beta = z2;
xcross = (beta*x(crossidx) + alpha*x(crossidx+1))./dz;
ycross = (beta*y(crossidx) + alpha*y(crossidx+1))./dz;
zcross = 0;
hold on; plot3(xcross, ycross, zcross, '*');
It should work for either positive to negative or negative to positive crossing. I wrote the code assuming only 1 zero crossing, but I think it would also work for any number of crossings.
I have 2 points A and B in a plane. What I need to find is the points w, x, y and z so that I can have a uniform bounding box.
The conditions are a line formed by wx and yz are parallel to AB.
Similarly wBz and xAy are parallel must be parallel.
Also note that angle zwx and wxy are right angles. Basically wxyz has to be a square.
z
/ /
B /
/ /
w /
/ y
/ /
/ A
/ /
x
Basically finding w, x, y and z is easy if line AB is parallel to x-axis or if AB is parallel to y-axis. I'm having trouble determining the points w,x,y and z when line AB is in an angle with x-axis (slope of line AB could be positive or negative).
Any comments/suggestions is highly appreciated. Thanks!
Treat A and B as vectors in your plane, (xa, ya) and (xb, yb). Take the vector difference, to generate a vector, C, that points from A to B.
C = A - B = (xa - xb, ya - yb) = (xc, yc)
Rotate this vector 90 degrees in each direction, and scale by a half, to get D = (xd, yd) and E = (xe, ye).
D = (-yc/2, +xc/2)
E = -D = (+yc/2, -xc/2)
Use vector arithmetic to get the four points of the square.
w = B + D
x = A + D
y = A + E
z = B + E
EDIT: Fat fingers.
EDIT2: Forgot the factor of a half.
EDIT3: Vector rotation reference, as requested.
To figure out the vector rotation, one can, in general, perform multiplication with a rotation matrix. In this case, the sin and cos factors of +/- pi/2 end up being +/- 1.
If matrix multiplication isn't your thing, draw on paper (or just imagine) a sample vector in any quadrant. Now rotate the paper 90 deg in either direction and see how the x and y components get swapped around and negated.
neirbowjs answer translated to more optimized solution, if optimization floats your boat.
Vars you know (Ax, Ay, Bx, By);
Vars you solve for (Wx, Wy, Xx, Xy, Yx, Yy,Zx, Zy);
float dx = By - Ay / 2;
float dy = Bx - Ax / 2;
float Wx = Ax - dx;
float Wy = Ay + dy;
float Zx = Ax + dx;
float Zy = Ay - dy;
float Xx = Bx - dx;
float Xy = By + dy;
float Yx = Bx + dx;
float Yy = By - dy;
I have the coordinates (x,y) of 2 points. I want to build the third point so that these 3 points make an equilateral triangle.
How can I calculate the third point?
Thank you
After reading the posts (specially vkit's) I produced this simple piece of code which will do the trick for one direction (remember that there are two points). The modification for the other case shold be trivial.
#include<stdio.h>
#include<math.h>
typedef struct{
double x;
double y;
} Point;
Point vertex(Point p1, Point p2){
double s60 = sin(60 * M_PI / 180.0);
double c60 = cos(60 * M_PI / 180.0);
Point v = {
c60 * (p1.x - p2.x) - s60 * (p1.y - p2.y) + p2.x,
s60 * (p1.x - p2.x) + c60 * (p1.y - p2.y) + p2.y
};
return v;
}
You could rotate the second point 60° around first to find the location of the third point.
Something like this:
//find offset from point 1 to 2
dX = x2 - x1;
dY = y2 - y1;
//rotate and add to point 1 to find point 3
x3 = (cos(60°) * dX - sin(60°) * dY) + x1;
y3 = (sin(60°) * dX + cos(60°) * dY) + y1;
Let's call your two points A and B. Bisect AB, call this point C. Find the slope of AB (YA-YB / XA-XB), call it m. Find the perpendicular to that (-1/m) and call it m2. Then compute a segment CD whose length is sin(60) * length(AB), at the slope m2 (there will be two such points, one to each side of AB). ABD is then your equilateral triangle.
That, obviously, is a "constructive" method. You should also be able to do it by solving a set of linear equations. I haven't tried to figure out the right system of equations for this case, but this approach tends to be somewhat more stable numerically, and has fewer special cases (e.g., with the constructive version, a slope of 0 has to be dealt with specially).
For BlueRaja's challenge go to end of post:
Answer using translation and rotation:
Says points are P(x1,y1) and Q(x2,y2).
Since it is graphics, you can use tranforms to get the point.
First translate axes so P is the origin.
Next rotate Q around P by 60 degrees (or -60 to get the other possible point).
This gives you the coordinates of the third point say R, when P is the origin.
Translate back and there you have it.
You can use standard graphics API which take care of precision etc issues for you. No headaches.
Of course you could do the math and actually come up with a formula and use that and that might be faster, but then the question might get closed as off-topic ;-)
To take up BlueRaja's challenge: Here is a method which does not use trigonometry.
Given points P(x1,y1) and Q(x2,y2)
Say the point we need (R) to find is (x3,y3).
Let T be midpoint of PQ.
We know the area of triangle PQR (as it is equilateral and we know the side)
and we know the area of triangle PRT (1/2 the earlier area).
Now area of a triangle can be written as a determinant having the co-ordinates as entries:
2*Area = |D|
where
| 1 x1 y1|
D = | 1 x2 y2|
| 1 x3 y3|
We have two such equations (which are linear), solve for x3 and y3.
pc <- c((x1+x2)/2,(y1+y2)/2) #center point
ov <- c(y2-y1,x1-x2) #orthogonal vector
p3 <- pc+sqrt(3/4)*ov #The 3dr point in equilateral triangle (center point + height of triangle*orthogonal vector)