I'm working on reverse-engineering a software system doing some computer graphics math. I've wound up in a weird situation where the following math is coming up:
M * X * M^-1 * A = B
Where M is a 3x3 constant invertible matrix, M^-1 is the inverse of M, X is a 3x3 matrix I can control, A is a 3x1 column vector I can also control. B is measurable.
How can I find what M is? I'm pretty sure that I could find the answer by expanding out all matrix components algebraically into a few massive equations - but it sure feels like there should be a more straightforward mechanism.
This looks like the Eigendecomposition of a matrix.
where the matrix A is decomposed into a diagonal matrix Λ and a vector of eigenvectors Q.
In your case you have
C = (M X M-1)
If you can confirm that indeed X in your case is a diagonal matrix, then you know M to be the eigenvectors of C. You can get C from C = A-1 B
As far as finding the eigenvectors, even for 3×3 matrices, is a genuinely hard problem. Typically you do a Shur Decomposition and recover the eigevectors.
I did find an online resource for the 3×3 problem, which I ported into C# for an other project. Original source here.
/// <summary>
/// Calculates the three eigenvalues analytically.
/// </summary>
/// <remarks>
/// Code taken from:
/// https://www.mpi-hd.mpg.de/personalhomes/globes/3x3/index.html
/// </remarks>
/// <returns>A vector containing the three eigenvalues.</returns>
public Vector3 GetEigenValues()
{
// Determine coefficients of characteristic polynomial. We write
// | A D F |
// A = | D* B E |
// | F* E* C |
var de = data.m_12 * data.m_23;
var dd = data.m_12 * data.m_12;
var ee = data.m_23 * data.m_23;
var ff = data.m_13 * data.m_13;
var m = data.m_11 + data.m_22 + data.m_33;
var c1 = (data.m_11 * data.m_22 + data.m_11 * data.m_33 + data.m_22 * data.m_33) - (dd + ee + ff);
var c0 = data.m_33 * dd + data.m_11 * ee + data.m_22 * ff - data.m_11 * data.m_22 * data.m_33 - 2.0 * data.m_13 * de;
var p = m * m - 3.0 * c1;
var q = m * (p - (3.0 / 2.0) * c1) - (27.0 / 2.0) * c0;
var sqrt_p = (float) Math.Sqrt(Math.Abs(p));
var sqrt_z = (float) Math.Sqrt(Math.Abs(27.0 * (0.25 * c1 * c1 * (p - c1) + c0 * (q + 27.0 / 4.0 * c0))));
var phi = (1 / 3f) * (float) Math.Atan2(sqrt_z, q);
var c = sqrt_p * (float) Math.Cos(phi);
var s = sqrt_p * (float)( Math.Abs(Math.Sin(phi))/ Math.Sqrt(3));
var w = (1 / 3f) * (m - c);
// sort the eigenvalues
if (c >= s)
{
return new Vector3(
w - s,
w + s,
w + c);
}
else if (c >= -s)
{
return new Vector3(
w - s,
w + c,
w + s);
}
else
{
return new Vector3(
w + c,
w - s,
w + s);
}
}
public Matrix3 GetEigenVectors() => GetEigenVectors(GetEigenValues());
public Matrix3 GetEigenVectors(Vector3 eigenValues)
{
Vector3 ev1 = GetEigenVector(eigenValues.X).Unit();
Vector3 ev2 = GetEigenVector(eigenValues.Y).Unit();
Vector3 ev3 = GetEigenVector(eigenValues.Z).Unit();
return FromColumns(ev1, ev2, ev3);
}
Vector3 GetEigenVector(float w)
{
return new Vector3(
data.m_12 * (data.m_23 - data.m_33 + w) - data.m_13 * (data.m_22 - data.m_23 - w)
+ data.m_22 * (data.m_33 - w) - data.m_23 * data.m_23 - w * (data.m_33 - w),
-data.m_11 * (data.m_23 - data.m_33 + w) + data.m_12 * (data.m_13 - data.m_33 + w)
- data.m_13 * data.m_13 + data.m_13 * data.m_23 + w * (data.m_23 - data.m_33 + w),
data.m_11 * (data.m_22 - data.m_23 - w) - data.m_12 * data.m_12 + data.m_12 * (data.m_13 + data.m_23)
+ data.m_13 * (w - data.m_22) - w * (data.m_22 - data.m_23 - w));
}
For your case you would do M=C.GetEigenVectors();
A final note here is that you can scale M up or down by a factor, and it wont change the equation since you are multiplying with M and M-1 at the same time. So you just need to find just one of the infinite matrices M that would make this equation work.
Related
I need to find the distance of O and N with the diagonales (with a 90° angle/ the shortest). I found a formula online, but why in this case, it does not return the good distance ?
And if possible, how to normalize the result (e.g. O is at20% of the diagonale?)
import numpy as np
import math
O = (1,3)
N = (3,2)
r = np.arange(24).reshape((6, 4))
def get_diagonal_distance(centroid, img_test):
x1, y1 = centroid
a, b = img.shape[1], img.shape[0]
c = np.sqrt(np.square(a) + np.square(b))
d = abs((a * x1 + b * y1 + c)) / (math.sqrt(a * a + b * b))
return d
print(f"diagonal d: {get_diagonal_distance(O, r): .4f}")
d = abs((a * x1 + b * y1 + c)) / (math.sqrt(a * a + b * b))
Your computation is wrong because a, b and c refer to the coefficients of the equation of the line ax+by+c=0
import numpy as np
O = (1,3)
N = (3,2)
M, L, I, H = (-1,-2), (3, -2), (3, 2), (-1, 2)
# Following your initial idea
def get_diagonal_distance(diagonal_extremes, point):
diagonal_vector = (diagonal_extremes[1][0] - diagonal_extremes[0][0],
diagonal_extremes[1][1] - diagonal_extremes[0][1])
a = diagonal_vector[1]
b = - diagonal_vector[0]
c = - diagonal_extremes[0][0]*a - diagonal_extremes[0][1]*b
x, y = point[0], point[1]
return abs((a * x + b * y + c)) / (np.sqrt(a * a + b * b))
# Taking advantage of numpy
def distance_from_diagonal(diagonal_extremes, point):
u = (diagonal_extremes[1][0] - diagonal_extremes[0][0],
diagonal_extremes[1][1] - diagonal_extremes[0][1])
v = (point[0] - diagonal_extremes[0][0],
point[1] - diagonal_extremes[0][1])
return np.cross(u, v) / np.linalg.norm(u)
print(f"diagonal d: {get_diagonal_distance((M, I), O): .4f}")
print(f"diagonal d: {distance_from_diagonal((M, I), O): .4f}")
I recently encountered the following
problem:
Given a set of points with height yᵢ, find the height of the line for which the average distance to points above equals the average distance to points below the line:
More abstract definition: Given a set of real valued data points Y = {y1, ..., yn}, find ȳ which splits Y into two sets Y⁺ = {y ∊ Y : y > ȳ} and Y⁻ = {y ∊ Y : y < ȳ} so that the average distance between ȳ and elements of Y⁺ equals the average distance between ȳ and elements of Y⁻.
Naive solution: Initialize ȳ with the average of Y, compute average upper and lower distances and iteratively move up or down depending on whether the upper or lower average distance is greater.
Question: This problem is pretty basic, so there is probably a better solution (?) Even a non-iterative algebraic algorithm?
As mentioned in the comment, if you know which points are above and below the line, then you can solve it like this:
a = number of points above the line
b = number of points below the line
sa = sum of all y above the line
sb = sum of all y below the line
Now we can create the following equation:
(sa - a * y) / a = (b * y - sb) / b | * a * b
sa * b - a * b * y = a * b * y - a * sb | + a * b * y + a * sb
sa * b + a * sb = 2 * a * b * y | / (2 * a * b)
==> y = (a * sb + b * sa) / (2 * a * b)
= sa / (2 * a) + sb / (2 * b)
= (sa / a + sb / b) / 2
If we interprete the result then we could say it is the average between the averages of the points above and below the line.
An iterative solution based on maraca's answer:
Initialize ȳ with the mean of the given values.
Split the given values into those above and below ȳ.
Calculate the new optimal ȳ for this split.
Repeat until ȳ converges.
This is slightly faster than the algorithm outlined in the question.
// Find mean with equal average distance to upper and lower values:
function findEqualAverageDistanceMean(values) {
let mean = values.reduce((a, b) => a + b) / values.length,
last = NaN;
// Iteratively equalize average distances:
while (last != mean) {
let lower_total = 0,
lower_n = 0,
upper_total = 0,
upper_n = 0;
for (let value of values) {
if (value > mean) {
upper_total += value;
++upper_n;
} else if (value < mean) {
lower_total += value;
++lower_n;
}
}
last = mean;
mean = (upper_total / upper_n + lower_total / lower_n) / 2;
}
return mean;
}
// Example:
let canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
points = Array.from({length: 100}, () => Math.random() ** 4),
mean = points.reduce((a, b) => a + b) / points.length,
equalAverageDistanceMean = findEqualAverageDistanceMean(points);
function draw(points, mean, equalAverageDistanceMean) {
for (let [i, point] of points.entries()) {
ctx.fillStyle = (point < equalAverageDistanceMean) ? 'red' : 'green';
ctx.fillRect(i * canvas.width / points.length, canvas.height * point, 3, 3);
}
ctx.fillStyle = 'black';
ctx.fillRect(0, canvas.height * mean, canvas.width, .5);
ctx.fillRect(0, canvas.height * equalAverageDistanceMean, canvas.width, 3);
}
draw(points, mean, equalAverageDistanceMean);
<canvas id="canvas" width="400" height="200">
Is there an algorithm for drawing grids of n-sided polygons. Here is an example for hexagons:
It's my solution using grid package.
library(grid)
hexagon_layer <- function(x, y, r) {
axis.x <- c(x, x + 3 * r, x + 6 * r, x + 9 * r, x + 12 * r, x + 15 *
r, x + 18 * r, x + 1.5 * r, x + 4.5 * r, x + 7.5 * r, x + 10.5 *
r, x + 13.5 * r, x + 16.5 * r, x + 1.5 * r, x + 4.5 * r, x + 7.5 *
r, x + 10.5 * r, x + 13.5 * r, x + 16.5 * r)
axis.y <- c(rep(y, 7), rep(y + r * sqrt(3)/2, 6), rep(y - r * sqrt(3)/2,
6))
hexagon <- function(x, y, r) {
grid.path(x = c(x + 0.5 * r, x + r, x + 0.5 * r, x - 0.5 * r, x -
r, x - 0.5 * r), y = c(y + r * sqrt(3)/2, y, y - r * sqrt(3)/2,
y - r * sqrt(3)/2, y, y + r * sqrt(3)/2))
}
for (i in 1:length(axis.x)) {
hexagon(axis.x[i], axis.y[i], r = 0.05)
next
}
}
for (i in seq(1, 21, by = 2)) {
hexagon_layer(x = 0.05, y = i * 0.05 * sqrt(3)/2, r = 0.05)
next
}
I need to split trapezoid in 2 part of given size with line, parallel basement. I need to get new h1 of new trapezoid.
For example I have trapezoid of area S and I want to split it in 2 trapezoids of areas S1 and S2.
S1 = aS; S2 = (1-a)S;
S1 = (a+z)*(h1)/2;
S2 = (b+z)*(1-h1)/2;
S1/S2 = KS;
To get new h1 I compare a and b, if a != b, I solve square equation and if a == b I work like with square. But sometimes I get mistakes because of rounding (for example when I solve this analytically I get a = b and program thinks a > b). How can I handle this? Or maybe there is another better way to split trapezoid?
Here is simplifyed code:
if (base > base_prev) {
b_t = base; // base of trapezoid
h = H; //height of trapezoid
a_t = base_prev; //another base of trapezoid
KS = S1 / S2;
a_x = (a_t - b_t) * (1 + KS) / h;
b_x = 2 * KS * b_t + 2 * b_t;
c_x = -(a_t * h + b_t * h);
h_tmp = (-b_x + sqrt(b_x * b_x - 4 * a_x * c_x)) / (2 * a_x);
if (h_tmp > h || h_tmp < 0)
h_tmp = (-b_x - sqrt(b_x * b_x - 4 * a_x * c_x)) / (2 * a_x);
} else if (base < base_prev) {
b_t = base_prev;
a_t = base;
KS = S1 / S2;
a_x = (a_t - b_t) * (1 + KS) / h;
b_x = 2 * KS * b_t + 2 * b_t;
c_x = -(a_t * h + b_t * h);
h_tmp = (-b_x + sqrt(b_x * b_x - 4 * a_x * c_x)) / (2 * a_x);
if (h_tmp > h || h_tmp < 0)
h_tmp = (-b_x - sqrt(b_x * b_x - 4 * a_x * c_x)) / (2 * a_x);
}
else {
KS = S1 / S2;
h_tmp = h * KS;
}
If you're dealing with catastrophic cancellation, one approach, dating back to a classic article by Forsythe, is to use the alternative solution form x = 2c/(-b -+ sqrt(b^2 - 4ac)) for the quadratic equation ax^2 + bx + c = 0. One way to write the two roots, good for b < 0, is
x = (-b + sqrt(b^2 - 4ac))/(2a)
x = 2c/(-b + sqrt(b^2 - 4ac)),
and another, good for b >= 0, is
x = 2c/(-b - sqrt(b^2 - 4ac))
x = (-b - sqrt(b^2 - 4ac))/(2a).
Alternatively, you could use the bisection method to obtain a reasonably good guess and polish it with Newton's method.
I want to find the coordinate of an unknown node which lie somewhere in the space which has its reference distance away from 3 or more nodes which all of them have known coordinate.
This problem is exactly like Trilateration as described here Trilateration.
However, I don't understand the part about "Preliminary and final computations" (refer to the wikipedia site). I don't get where I could find P1, P2 and P3 just so I can put to those equation?
Thanks
Trilateration is the process of finding the center of the area of intersection of three spheres. The center point and radius of each of the three spheres must be known.
Let's consider your three example centerpoints P1 [-1,1], P2 [1,1], and P3 [-1,-1]. The first requirement is that P1' be at the origin, so let us adjust the points accordingly by adding an offset vector V [1,-1] to all three:
P1' = P1 + V = [0, 0]
P2' = P2 + V = [2, 0]
P3' = P3 + V = [0,-2]
Note: Adjusted points are denoted by the ' (prime) annotation.
P2' must also lie on the x-axis. In this case it already does, so no adjustment is necessary.
We will assume the radius of each sphere to be 2.
Now we have 3 equations (given) and 3 unknowns (X, Y, Z of center-of-intersection point).
Solve for P4'x:
x = (r1^2 - r2^2 + d^2) / 2d //(d,0) are coords of P2'
x = (2^2 - 2^2 + 2^2) / 2*2
x = 1
Solve for P4'y:
y = (r1^2 - r3^2 + i^2 + j^2) / 2j - (i/j)x //(i,j) are coords of P3'
y = (2^2 - 2^2 + 0 + -2^2) / 2*-2 - 0
y = -1
Ignore z for 2D problems.
P4' = [1,-1]
Now we translate back to original coordinate space by subtracting the offset vector V:
P4 = P4' - V = [0,0]
The solution point, P4, lies at the origin as expected.
The second half of the article is describing a method of representing a set of points where P1 is not at the origin or P2 is not on the x-axis such that they fit those constraints. I prefer to think of it instead as a translation, but both methods will result in the same solution.
Edit: Rotating P2' to the x-axis
If P2' does not lie on the x-axis after translating P1 to the origin, we must perform a rotation on the view.
First, let's create some new vectors to use as an example:
P1 = [2,3]
P2 = [3,4]
P3 = [5,2]
Remember, we must first translate P1 to the origin. As always, the offset vector, V, is -P1. In this case, V = [-2,-3]
P1' = P1 + V = [2,3] + [-2,-3] = [0, 0]
P2' = P2 + V = [3,4] + [-2,-3] = [1, 1]
P3' = P3 + V = [5,2] + [-2,-3] = [3,-1]
To determine the angle of rotation, we must find the angle between P2' and [1,0] (the x-axis).
We can use the dot product equality:
A dot B = ||A|| ||B|| cos(theta)
When B is [1,0], this can be simplified: A dot B is always just the X component of A, and ||B|| (the magnitude of B) is always a multiplication by 1, and can therefore be ignored.
We now have Ax = ||A|| cos(theta), which we can rearrange to our final equation:
theta = acos(Ax / ||A||)
or in our case:
theta = acos(P2'x / ||P2'||)
We calculate the magnitude of P2' using ||A|| = sqrt(Ax + Ay + Az)
||P2'|| = sqrt(1 + 1 + 0) = sqrt(2)
Plugging that in we can solve for theta
theta = acos(1 / sqrt(2)) = 45 degrees
Now let's use the rotation matrix to rotate the scene by -45 degrees.
Since P2'y is positive, and the rotation matrix rotates counter-clockwise, we'll use a negative rotation to align P2 to the x-axis (if P2'y is negative, don't negate theta).
R(theta) = [cos(theta) -sin(theta)]
[sin(theta) cos(theta)]
R(-45) = [cos(-45) -sin(-45)]
[sin(-45) cos(-45)]
We'll use double prime notation, '', to denote vectors which have been both translated and rotated.
P1'' = [0,0] (no need to calculate this one)
P2'' = [1 cos(-45) - 1 sin(-45)] = [sqrt(2)] = [1.414]
[1 sin(-45) + 1 cos(-45)] = [0] = [0]
P3'' = [3 cos(-45) - (-1) sin(-45)] = [sqrt(2)] = [ 1.414]
[3 sin(-45) + (-1) cos(-45)] = [-2*sqrt(2)] = [-2.828]
Now you can use P1'', P2'', and P3'' to solve for P4''. Apply the reverse rotation to P4'' to get P4', then the reverse translation to get P4, your center point.
To undo the rotation, multiply P4'' by R(-theta), in this case R(45). To undo the translation, subtract the offset vector V, which is the same as adding P1 (assuming you used -P1 as your V originally).
This is the algorithm I use in a 3D printer firmware. It avoids rotating the coordinate system, but it may not be the best.
There are 2 solutions to the trilateration problem. To get the second one, replace "- sqrtf" by "+ sqrtf" in the quadratic equation solution.
Obviously you can use doubles instead of floats if you have enough processor power and memory.
// Primary parameters
float anchorA[3], anchorB[3], anchorC[3]; // XYZ coordinates of the anchors
// Derived parameters
float Da2, Db2, Dc2;
float Xab, Xbc, Xca;
float Yab, Ybc, Yca;
float Zab, Zbc, Zca;
float P, Q, R, P2, U, A;
...
inline float fsquare(float f) { return f * f; }
...
// Precompute the derived parameters - they don't change unless the anchor positions change.
Da2 = fsquare(anchorA[0]) + fsquare(anchorA[1]) + fsquare(anchorA[2]);
Db2 = fsquare(anchorB[0]) + fsquare(anchorB[1]) + fsquare(anchorB[2]);
Dc2 = fsquare(anchorC[0]) + fsquare(anchorC[1]) + fsquare(anchorC[2]);
Xab = anchorA[0] - anchorB[0];
Xbc = anchorB[0] - anchorC[0];
Xca = anchorC[0] - anchorA[0];
Yab = anchorA[1] - anchorB[1];
Ybc = anchorB[1] - anchorC[1];
Yca = anchorC[1] - anchorA[1];
Zab = anchorB[2] - anchorC[2];
Zbc = anchorB[2] - anchorC[2];
Zca = anchorC[2] - anchorA[2];
P = ( anchorB[0] * Yca
- anchorA[0] * anchorC[1]
+ anchorA[1] * anchorC[0]
- anchorB[1] * Xca
) * 2;
P2 = fsquare(P);
Q = ( anchorB[1] * Zca
- anchorA[1] * anchorC[2]
+ anchorA[2] * anchorC[1]
- anchorB[2] * Yca
) * 2;
R = - ( anchorB[0] * Zca
+ anchorA[0] * anchorC[2]
+ anchorA[2] * anchorC[0]
- anchorB[2] * Xca
) * 2;
U = (anchorA[2] * P2) + (anchorA[0] * Q * P) + (anchorA[1] * R * P);
A = (P2 + fsquare(Q) + fsquare(R)) * 2;
...
// Calculate Cartesian coordinates given the distances to the anchors (La, Lb and Lc)
// First calculate PQRST such that x = (Qz + S)/P, y = (Rz + T)/P.
// P, Q and R depend only on the anchor positions, so they are pre-computed
const float S = - Yab * (fsquare(Lc) - Dc2)
- Yca * (fsquare(Lb) - Db2)
- Ybc * (fsquare(La) - Da2);
const float T = - Xab * (fsquare(Lc) - Dc2)
+ Xca * (fsquare(Lb) - Db2)
+ Xbc * (fsquare(La) - Da2);
// Calculate quadratic equation coefficients
const float halfB = (S * Q) - (R * T) - U;
const float C = fsquare(S) + fsquare(T) + (anchorA[1] * T - anchorA[0] * S) * P * 2 + (Da2 - fsquare(La)) * P2;
// Solve the quadratic equation for z
float z = (- halfB - sqrtf(fsquare(halfB) - A * C))/A;
// Substitute back for X and Y
float x = (Q * z + S)/P;
float y = (R * z + T)/P;
Here are the Wikipedia calculations, presented in an OpenSCAD script, which I think helps to understand the problem in a visual wayand provides an easy way to check that the results are correct. Example output from the script
// Trilateration example
// from Wikipedia
//
// pA, pB and pC are the centres of the spheres
// If necessary the spheres must be translated
// and rotated so that:
// -- all z values are 0
// -- pA is at the origin
pA = [0,0,0];
// -- pB is on the x axis
pB = [10,0,0];
pC = [9,7,0];
// rA , rB and rC are the radii of the spheres
rA = 9;
rB = 5;
rC = 7;
if ( pA != [0,0,0]){
echo ("ERROR: pA must be at the origin");
assert(false);
}
if ( (pB[2] !=0 ) || pC[2] !=0){
echo("ERROR: all sphere centers must be in z = 0 plane");
assert(false);
}
if (pB[1] != 0){
echo("pB centre must be on the x axis");
assert(false);
}
// show the spheres
module spheres(){
translate (pA){
sphere(r= rA, $fn = rA * 10);
}
translate(pB){
sphere(r = rB, $fn = rB * 10);
}
translate(pC){
sphere (r = rC, $fn = rC * 10);
}
}
function unit_vector( v) = v / norm(v);
ex = unit_vector(pB - pA) ;
echo(ex = ex);
i = ex * ( pC - pA);
echo (i = i);
ey = unit_vector(pC - pA - i * ex);
echo (ey = ey);
d = norm(pB - pA);
echo (d = d);
j = ey * ( pC - pA);
echo (j = j);
x = (pow(rA,2) - pow(rB,2) + pow(d,2)) / (2 * d);
echo( x = x);
// size of the cube to subtract to show
// the intersection of the spheres
cube_size = [10,10,10];
if ( ((d - rA) >= rB) || ( rB >= ( d + rA)) ){
echo ("Error Y not solvable");
}else{
y = (( pow(rA,2) - pow(rC,2) + pow(i,2) + pow(j,2)) / (2 * j))
- ( i / j) * x;
echo(y = y);
zpow2 = pow(rA,2) - pow(x,2) - pow(y,2);
if ( zpow2 < 0){
echo ("z not solvable");
}else{
z = sqrt(zpow2);
echo (z = z);
// subtract a cube with one of its corners
// at the point where the sphers intersect
difference(){
spheres();
translate ([x,y - cube_size[1],z]){
cube(cube_size);
}
}
translate ([x,y - cube_size[1],z]){
%cube(cube_size);
}
}
}