Relative position of a point within a quadrilateral - math

I am trying to find the easiest way to determine a relative position of a point within a quadrilateral. The known are (see figure) the positions of points 1, 2, 3, 4 and 5 in the xy-coordinate system: x1, y1, x2, y2, x3, y3, x4, y4, x5, y5.
Also known are the positions of points 1, 2, 3, and 4 in the ξ-η coordinate systems (see figure).
From this data, I want to determine what are the ξ and η for point 5.
Results
Thank you to all who anwsered! I find the solution by #dbc and #agentp similar. Also I find this solution better than the perspective transformation solution by #MBo, since I do not have to compute the inverse of a matrix (Ax=B --> x=inv(A)*B).
I get the following result for:
u = 0.5 * (ξ + 1)
v = 0.5 * (η + 1)
In my case all points are within the rectangle, therefore u>0 and v>0.

What you have here is a 2d bilinear blended surface. For simplicity, let's change its coordinates to range from zero to one:
u = 0.5 * (ξ + 1)
v = 0.5 * (η + 1)
In that case, the surface evaluator can be expressed as
F(u, v) = P1 + u * (P2 - P1) + v * ((P4 + u * (P3 - P4)) - (P1 + u * (P2 - P1)))
I.e., for a given u, construct a line passing through the following two points:
Pv0 = P1 + u * (P2 - P1);
Pv1 = P4 + u * (P3 - P4);
then interpolate between then for given v
F(u, v) = Pv0 + v * (Pv1 - Pv0)
What you seek are values (u,v) such that F(u, v) = P5. This will occur for given u when the line from Pv0 to Pv1 passes through P5, which will occur when P5 - Pv0 is parallel to Pv1 - Pv0 -- i.e. when their 2d cross is zero:
cross2d(P5 - Pv0, Pv1 - Pv0) = 0
⇒
cross2d(P5 - (P1 + u * (P2 - P1)),
P4 + u * (P3 - P4) - (P1 + u * (P2 - P1))) = 0
Now, the 2d cross of two 2d vectors A ⨯ B is given by Ax*By - Ay*Bx, so that equation becomes
(x5 - (x1 + u * (x2 - x1))) * (y4 + u * (y3 - y4) - (y1 + u * (y2 - y1))) - (y5 - (y1 + u * (y2 - y1))) * (x4 + u * (x3 - x4) - (x1 + u * (x2 - x1))) = 0
Expanding this expression out and collecting collecting together terms in u, we get
u^2 * (x1*y3 - x1*y4 - x2*y3 + x2*y4 + (-x3)*y1 + x3*y2 + x4*y1 - x4*y2)
+ u * (-x1*y3 + 2*x1*y4 - x1*y5 - x2*y4 + x2*y5 + x3*y1 - x3*y5 - 2*x4*y1 + x4*y2 + x4*y5 + x5*y1 - x5*y2 + x5*y3 - x5*y4)
+ (-x1*y4 + x1*y5 + x4*y1 - x4*y5 - x5*y1 + x5*y4)
= 0
This is now a quadratic equation over u, and can be solved as such. Note that in cases where the top and bottom edges of your quadrilateral are parallel then the quadratic devolves into a linear equation; your quadratic equation solver must needs handle this.
double a = (x1 * y3 - x1 * y4 - x2 * y3 + x2 * y4 + (-x3) * y1 + x3 * y2 + x4 * y1 - x4 * y2);
double b = (-x1 * y3 + 2 * x1 * y4 - x1 * y5 - x2 * y4 + x2 * y5 + x3 * y1 - x3 * y5 - 2 * x4 * y1 + x4 * y2 + x4 * y5 + x5 * y1 - x5 * y2 + x5 * y3 - x5 * y4);
double c = (-x1 * y4 + x1 * y5 + x4 * y1 - x4 * y5 - x5 * y1 + x5 * y4);
double[] solutions = Quadratic.Solve(a, b, c);
There may be more than one solution. There might also be no solutions for a degenerate quadrilateral.
Having solved for value(s) of u, finding the equivalent v is straightforward. Given points
Pv0 = P1 + u * (P2 - P1);
Pv1 = P4 + u * (P3 - P4);
you seek v such that
v * (Pv1 - Pv0) = P5 - Pv0;
Pick the coordinate index 0 or 1 such that |(Pv1 - Pv0)[index]| is maximized. (If both coordinates are almost zero, then give up -- there's no solution for this specific u. Then set
v = (P5 - Pv0)[index] / (Pv1 - Pv0)[index];
Finally, if you have more that one solution, prefer a solution inside the [u, v] boundaries of the blend. Then finally set
ξ = 2 * u - 1;
η = 2 * v - 1;

This looks like a standard finite element parameterization
(The question doesn't specify a particular mapping, but I imagine someone might be interested in this specific case)
{x, y} == (
(1 - eta) (1 - ci) {p1x, p1y} +
(1 - eta) (1 + ci) {p2x, p2y} +
(1 + eta) (1 + ci) {p3x, p3y} +
(1 + eta) (1 - ci) {p4x, p4y} )/4
This can be solved in closed form for {eta,ci}, but the expression is pretty unwieldy to post.
In practice, compute these constants:
ax = p1x + p2x + p3x + p4x
bx = p1x - p2x - p3x + p4x
cx = p1x + p2x - p3x - p4x
dx = p1x - p2x + p3x - p4x
ay = p1y + p2y + p3y + p4y
by = p1y - p2y - p3y + p4y
cy = p1y + p2y - p3y - p4y;
dy = p1y - p2y + p3y - p4y;
Solve this quadratic for eta :
(ax by - bx ay) - 4 (by x - bx y) +
eta (dx ay - cx by + bx cy - ax dy + 4 (x dy - dx y)) +
eta^2 (cx dy - dx cy) == 0
then get ci as:
ci = ((-ax + eta cx + 4 x)/(-bx + eta dx))
If the polygon is not too distorted just one of the solutions will satisfy -1<eta<1 and -1<ci<1

Referring to the self-answer of #blaz (please vote up the answers of #blaze, #dbc and #agentp)
For everybody who is not willing to copy the formulas by hand, here is the formula as C# code:
double v_sqrt = Math.Sqrt(
4 * (
(x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4)) * (x4 * (-1 * y + y1) + x1 * (y - y4) + x * (-1 * y1 + y4)) +
Math.Pow(
(x3 * y - x4 * y - x3 * y1 + 2 * x4 * y1 - x4 * y2 + x1 * (y + y3 - 2 * y4) + x2 * (-1 * y + y4) + x * (-1 * y1 + y2 - y3 + y4))
, 2)
);
double u_sqrt = Math.Sqrt(
4 * ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4))
* (
x4 * (-1 * y + y1) + x1 * (y - y4) + x * (-1 * y1 + y4)
) +
Math.Pow(
(x3 * y - x4 * y - x3 * y1 + 2 * x4 * y1 - x4 * y2 + x1 * (y + y3 - 2 * y4) + x2 * (-1 * y + y4) + x * (-1 * y1 + y2 - y3 + y4))
, 2)
);
double k = 1 / (2 * ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4)));
double l = 1 / (2 * ((x1 - x4) * (y2 - y3) - (x2 - x3) * (y1 - y4)));
///////////////////////////////////////////////////////////////////////////////////////////////
double v1 = l *
(x2 * y - x3 * y + x4 * y + x * y1 - 2 * x2 * y1 + x3 * y1 - x * y2 - x4 * y2 + x * y3 - x1 * (y - 2 * y2 + y3) - x * y4 + x2 * y4 +
v_sqrt);
///////////////////////////////////////////////////////////////////////////////////////////////
double u1 = -1 * k *
(-x2 * y + x3 * y - x * y1 - x3 * y1 + 2 * x4 * y1 + x * y2 - x4 * y2 - x * y3 + x1 * (y + y3 - 2 * y4) + x * y4 + x2 * y4 +
u_sqrt);
double v2 = -1 * l *
(x1 * y + x3 * y - x4 * y - x * y1 - 2 * x3 * y1 + x * y2 - -2 * x1 * y2 + x4 * y2 - x * y3 + x1 * y3 + x * y4 - x2 * (y - 2 * y1 + y4) +
v_sqrt);
/////////////////////////////////////////////////////////////////////////////////////////////////
double u2 = k *
(x2 * y - x3 * y + x4 * y + x * y1 + x3 * y1 - 2 * x4 * y1 - x * y2 + x4 * y2 + x * y3 - x1 * (y + y3 - 2 * y4) - x * y4 - x2 * y4 +
u_sqrt);
In most cases it is u1 and v1 so there should not be the need for computing the other ones.
I used it to calibrate the coordinates of a Pegasus Air-Pen device (ultrasonic stylus) on a sheet of paper. It does work best if your coordinates for point 1 to 5 are also >= 0.
Sry for posting this as an answer but it is too long for a comment and I think it is a valuable help for this post as it would be for me.

You need to calculate a matrix of perspective transformation, that maps 4 points of source quadrilateral to 4 points of destination quadrilateral (example) (more mathemathics), then apply this transformation to coordinates of 5th point (multiply matrix by coordinate vector)

Related

Find xy coordinates of a point knowing its distance to other 2 points

I'm writing a code in R to calculate the xy coordinates of point, using the law of cosines.
I have two reference points (1 and 2) which the xy coordinates are known. I want to find the coordinates of the other point (3). I know the distances 3-1, 3-2 and 1-2, but I don't know the angles between them.
Thanks in advance for any help!
I've tried some trigonometric equations I've found on web and Rohlf&Archie 1978 paper, but they don't work.
You haven't told us your set-up exactly, but it sounds as though you have two known x, y, co-ordinates:
x1 <- 1
x2 <- 5
y1 <- 3
y2 <- 6
And known distances between these two points plus a third point:
d12 <- 5
d13 <- 8
d23 <- 5
We can draw them like this:
plot(c(x1, x2), c(y1, y2), xlim = c(0, 12), ylim = c(0, 12))
text(c(x1, x2), c(y1, y2) + 0.5, labels = c('1', '2'))
Now, it's obviously easy to calculate the angle between the horizontal line and the segment joining points 1 and two - it's just the arctangent of the slope:
abline(h = y1, lty = 2)
theta <- atan2(y2 - y1, x2 - x1)
segments(x1, y1, x1 + d12 * cos(theta), y1 + d12 * sin(theta), lty = 2)
Now, although we don't know where point 3 is, we can use the law of cosines to calculate the angle 3-1-2 like this:
angle_312 <- acos((d12^2 + d13^2 - d23^2)/(2 * d12 * d13))
To get this in terms of angle from the horizontal we can do:
angle_13 <- angle_312 - theta
This allows us to work out the co-ordinates of point 3:
x3 <- x1 + d13 * cos(angle_13)
y3 <- y1 + d13 * sin(angle_13)
We can draw this point on our plot as follows:
points(x3, y3)
text(x3, y3 + 0.5, '3')
And we can show that it is correct by drawing circles of the correct radius around points one and two. Point 3 should be at the meeting point of the two circles:
polygon(x1 + dist_1_3 * cos(seq(0, 2 * pi, length = 100)),
y1 + dist_1_3 * sin(seq(0, 2 * pi, length = 100)), lty = 2)
polygon(x2 + dist_2_3 * cos(seq(0, 2 * pi, length = 100)),
y2 + dist_2_3 * sin(seq(0, 2 * pi, length = 100)), lty = 2)
Note that there is a second solution at the other point where the circles meet: in this case we would get that by changing angle_13 <- angle_312 - theta to angle_13 <- angle_312 + theta:
angle_13 <- angle_312 + theta
x3 <- x1 + d13 * cos(angle_13)
y3 <- y1 + d13 * sin(angle_13)
points(x3, y3)
text(x3, y3 + 0.5, '3')
Suppose we are using the following conditions:
pointA <- c(x1 = 1, y1 = 1)
pointC <- c(x2 = 4, y2 = 1)
AC <- sqrt(
(pointA[["x1"]] - pointC[["x2"]])^2 +
(pointA[["y1"]] - pointC[["y2"]])^2
)
AB <- 4
BC <- 5
We are looking for coordinates of point B (points B1 and B2) (x3 and y3 / x3' and y3').
First we find cos and sin of angle C (ACB1 == ACB2 as CB1==CB2==a; AB1==AB2==c and AC=b is common):
cosC <-(AC^2 + BC^2 - AB^2) / (2*AC*BC)
sinC <- sqrt(1-cosC^2)
It is obvious that there are two possible solutions for the conditions.
Then the points will be
pointB1 <- c(x3 = pointC[["x2"]] + BC*cosC,
y3 = pointC[["y2"]] + BC*sinC)
pointB2 <- c(x3 = pointC[["x2"]] - BC*cosC,
y3 = pointC[["y2"]] - BC*sinC)
Now we can check the results:
> sqrt(
+ (pointB1[["x3"]] - pointC[["x2"]])^2 +
+ (pointB1[["y3"]] - pointC[["y2"]])^2
+ )
[1] 5
> sqrt(
+ (pointB2[["x3"]] - pointC[["x2"]])^2 +
+ (pointB2[["y3"]] - pointC[["y2"]])^2
+ )
[1] 5
The advantage of this solution that we do not call low precision acosfunction as well as other trigonometric functions. And use cosC / sinC as temporary variables only.

how to find the center of rotation of a point in 2d space

I have a 2d point that rotates around some center of rotation other than the origin.
how can calculate the center of rotation given only the different positions of the point?
To get wanted result, you need three points.
Having them as points at circumference, you can calculate center of circle.
Look here or get formulas from wiki
D = |x1 y1 1|
|x2 y2 1|
|x3 y3 1|
Dx = |x1^2 + y1^2 y1 1|
|x2^2 + y2^2 y2 1|
|x3^2 + y3^2 y3 1|
Dy = |x1^2 + y1^2 x1 1|
|x2^2 + y2^2 x2 1|
|x3^2 + y3^2 x3 1|
D = x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)
Dx = (x1^2 + y1^2) * (y2 - y3) + (x2^2 + y2^2) * (y3 - y1) + (x3^2 + y3^2) * (y1 - y2)
Dy = (x1^2 + y1^2) * (x2 - x3) + (x2^2 + y2^2) * (x3 - x1) + (x3^2 + y3^2) * (x1 - x2)
Center coordinates
Cx = Dx / (2*D)
Cy = - Dy / (2*D)

How does B(t) of a bezier curve move when P1/P2 move along tangent?

When P1 changes from (0,4) to (0,2), Q1(t=0.5) and Q2(t=0.6) move 0.75 and 0.576 respectively. How can I calculate for any B(t) the distance it moves when P1 or P2 move along (Start--P1) or (P2--End) respectively?
Just write Bezier curve expression:
B(t) = P0 * (1-t)^3 + P1 * 3 * t * (1-t)^2 + P2 * 3 * t^2 * (1-t) + P3 * t^3
Let's P1' is new position of P1 control point. Only the second term will be changed, so
DeltaB(t) = B'(t) - B(t) = (P1' - P1) * 3 * t * (1-t)^2
if P1' lies on P0-P1, then
P1' = P0 + (P1 - P0) * u
DeltaB(t) = (P0 + (P1 - P0) * u - P1) * 3 * t * (1-t)^2 =
(P0 - P1) * (1 - u) * 3 * t * (1-t)^2
For your example data
u = 0.5
(P0 - P1) * (1 - u) = (0, -2) // (x,y) components of vector
DeltaB(0.5) = (0, -2 * 3 * 0.5 * 0.25) = (0, -0.75)
DeltaB(0.6) = (0, -2 * 3 * 0.6 * 0.4 * 0.4) = (0, -0.576)

find inverse (x, y pos) point in circle

I have a fixed point (x1, y1) and a moving/rotating point (x2, y2), how do I find the tangent inverse point (x3, y3)
My circle radius is 40.
Assuming p1 and p2 are 2-vectors, the following will do it.
v12 = normalize(p2 - p1) // the unit vector from p1 to p2
p3 = p1 - 40 * v12 // 40 away from p1 in the direction opposite p2
The value of normalize(u) is simply u / sqrt(u.x * u.x + u.y * u.y).
achieved it this way, modified to be rotated to any angle
http://jsfiddle.net/christianpugliese/g2Lk9k12/1/
var dx = x2 - x1;
var dy = y2 - y1;
var radianAngle = Math.atan2(dy, dx);
var diameter = -80;
p3x = x1 + diameter * Math.cos(radianAngle);
p3y = y1 + diameters * Math.sin(radianAngle);

Math Error in Bezier curve

I'm trying to get plots to generate a cubic Bezier Curve, and I've managed to be able to generate linear and quad easily, but I keep getting an error with my cubic formula,
Linear formula:x = (1-t)*(p0x + (t * p1x))
quad formula:x = (1-t)^2 * p0x + 2*(1-t) * t * p1x + t^2 * p2x
cubic formula:x = (1–t)^3 * p0x + 3*(1–t)^2 * t * p1x + 3*(1–t)*t^2 * p2x + t^3 * p3x
Though the quad and cubic formula are very similar, the cubic errors "')' expected near '–'". How can this be fixed?
I'm programming this in Lua.
The subtraction signs in your cubic formula aren't plain -:
>>> s = """
... linear formula: `x = (1-t)*(p0x + (t * p1x))`
... quad formula: `x = (1-t)^2 * p0x + 2*(1-t) * t * p1x + t^2 * p2x`
... cubic formula: `x = (1–t)^3 * p0x + 3*(1–t)^2 * t * p1x + 3*(1–t)*t^2 * p2x + t^3 * p3x`
...
... """
>>> for line in s.splitlines():
... print repr(line)
...
''
'linear formula: `x = (1-t)*(p0x + (t * p1x))`'
'quad formula: `x = (1-t)^2 * p0x + 2*(1-t) * t * p1x + t^2 * p2x`'
'cubic formula: `x = (1\xe2\x80\x93t)^3 * p0x + 3*(1\xe2\x80\x93t)^2 * t * p1x + 3*(1\xe2\x80\x93t)*t^2 * p2x + t^3 * p3x`'
''
They're actually U+2013 –, which is EN DASH. Fix those and it should be fine.

Resources