Skip to content

Commit 9cc40cd

Browse files
author
Fraser Greenroyd
authored
Geometry_Engine: Singular Value Decomposition implemented and applied to FitLine (#3280)
2 parents 3ec20aa + 4afbe28 commit 9cc40cd

File tree

2 files changed

+345
-84
lines changed

2 files changed

+345
-84
lines changed

Geometry_Engine/Compute/FitLine.cs

Lines changed: 12 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
2121
*/
2222

23+
using BH.oM.Base;
2324
using BH.oM.Base.Attributes;
2425
using BH.oM.Geometry;
25-
using System;
2626
using System.Collections.Generic;
2727
using System.ComponentModel;
2828
using System.Linq;
@@ -32,7 +32,7 @@ namespace BH.Engine.Geometry
3232
public static partial class Compute
3333
{
3434
/***************************************************/
35-
/**** public Methods - Vectors ****/
35+
/**** Public Methods ****/
3636
/***************************************************/
3737

3838
[Description("Fits a line into a set of points using Orthogonal Least Squares algorithm.")]
@@ -46,92 +46,20 @@ public static Line FitLine(this IEnumerable<Point> points, double tolerance = To
4646
if (n < 2)
4747
return null;
4848

49-
// Three cases: collinear, coplanar in XYZ and general covered separately
50-
// Required due to the fact that general solution degrades to zero length line for edge cases
5149
Point C = points.Average();
52-
if (asList.IsCollinear(tolerance))
53-
{
54-
List<Point> sorted = asList.SortCollinear(tolerance);
55-
return new Line { Start = sorted[0], End = sorted[sorted.Count - 1] };
56-
}
57-
else if (points.All(x => Math.Abs(C.Z - x.Z) < tolerance))
50+
51+
double[,] A = new double[n,3];
52+
for (int i = 0; i < n; i++)
5853
{
59-
// Based on https://iojes.net/Makaleler/386bbbe6-b98d-4766-a717-2c945a97f267.pdf
60-
double sumX = points.Sum(x => x.X);
61-
double sumY = points.Sum(x => x.Y);
62-
double sumXsq = points.Sum(x => x.X * x.X);
63-
double sumYsq = points.Sum(x => x.Y * x.Y);
64-
double sumXY = points.Sum(x => x.X * x.Y);
65-
double p1 = sumX * sumX - sumY * sumY - n * (sumXsq - sumYsq);
66-
double p2 = n * sumXY - sumX * sumY;
67-
double b = (p1 + Math.Sqrt(p1 * p1 + 4 * p2 * p2)) / (2 * p2);
68-
Vector dir = new Vector { X = 1, Y = b };
69-
return new Line { Start = C, End = C + dir };
54+
A[i, 0] = asList[i].X - C.X;
55+
A[i, 1] = asList[i].Y - C.Y;
56+
A[i, 2] = asList[i].Z - C.Z;
7057
}
71-
else
72-
{
73-
// Based on https://www.scribd.com/doc/31477970/Regressions-et-trajectoires-3D
74-
double xx = 0.0; double xy = 0.0; double xz = 0.0;
75-
double yy = 0.0; double yz = 0.0; double zz = 0.0;
76-
77-
foreach (Point P in points)
78-
{
79-
xx += P.X * P.X;
80-
xy += P.X * P.Y;
81-
xz += P.X * P.Z;
82-
yy += P.Y * P.Y;
83-
yz += P.Y * P.Z;
84-
zz += P.Z * P.Z;
85-
}
86-
87-
double Sxx = xx / n - C.X * C.X;
88-
double Sxy = xy / n - C.X * C.Y;
89-
double Sxz = xz / n - C.X * C.Z;
90-
double Syy = yy / n - C.Y * C.Y;
91-
double Syz = yz / n - C.Y * C.Z;
92-
double Szz = zz / n - C.Z * C.Z;
93-
94-
double theta = Math.Atan(2 * Sxy / (Sxx - Syy)) * 0.5;
95-
double stheta = Math.Sin(theta);
96-
double ctheta = Math.Cos(theta);
97-
double K11 = (Syy + Szz) * ctheta * ctheta + (Sxx + Szz) * stheta * stheta - 2 * Sxy * ctheta * stheta;
98-
double K22 = (Syy + Szz) * stheta * stheta + (Sxx + Szz) * ctheta * ctheta + 2 * Sxy * ctheta * stheta;
99-
double K12 = -Sxy * (ctheta * ctheta - stheta * stheta) + (Sxx - Syy) * ctheta * stheta;
100-
double K10 = Sxz * ctheta + Syz * stheta;
101-
double K01 = -Sxz * stheta + Syz * ctheta;
102-
double K00 = Sxx + Syy;
10358

104-
double c0 = K01 * K01 * K11 + K10 * K10 * K22 - K00 * K11 * K22;
105-
double c1 = K00 * K11 + K00 * K22 + K11 * K22 - K01 * K01 - K10 * K10;
106-
double c2 = -K00 - K11 - K22;
107-
108-
double p = c1 - c2 * c2 / 3;
109-
double q = c2 * c2 * c2 * 2 / 27 - c1 * c2 / 3 + c0;
110-
double R = q * q * 0.25 + p * p * p / 27;
111-
112-
double sqrDeltaM;
113-
double cc = -c2 / 3;
114-
115-
if (R > tolerance)
116-
sqrDeltaM = cc + Math.Pow(-q * 0.5 + Math.Sqrt(R), 1.0 / 3.0) + Math.Pow(-q * 0.5 - Math.Sqrt(R), 1.0 / 3.0);
117-
else
118-
{
119-
double rho = Math.Sqrt(-p * p * p / 27);
120-
double fi = Math.Acos(-q / rho * 0.5);
121-
double doubleRhoRoot = 2 * Math.Pow(rho, 1.0 / 3.0);
122-
double minCos = Math.Min(Math.Cos(fi / 3), Math.Min(Math.Cos((fi + 2 * Math.PI) / 3), Math.Cos((fi + 4 * Math.PI))));
123-
sqrDeltaM = cc + doubleRhoRoot * minCos;
124-
}
125-
126-
double a = -K10 / (K11 - sqrDeltaM) * ctheta + K01 / (K22 - sqrDeltaM) * stheta;
127-
double b = -K10 / (K11 - sqrDeltaM) * stheta - K01 / (K22 - sqrDeltaM) * ctheta;
128-
double u = ((1 + b * b) * C.X - a * b * C.Y + a * C.Z) / (1 + a * a + b * b);
129-
double v = (-a * b * C.X + (1 + a * a) * C.Y + b * C.Z) / (1 + a * a + b * b);
130-
double w = (a * C.X + b * C.Y + (a * a + b * b) * C.Z) / (1 + a * a + b * b);
131-
132-
Point H = new Point { X = u, Y = v, Z = w };
133-
return new Line { Start = C + (C - H), End = H };
134-
}
59+
Output<double[,], double[], double[,]> svd = A.SingularValueDecomposition();
60+
double[,] Vh = svd.Item3;
61+
Vector dir = new Vector { X = Vh[0, 0], Y = Vh[0, 1], Z = Vh[0, 2] };
62+
return new Line { Start = C - dir, End = C + dir };
13563
}
13664

13765
/***************************************************/

0 commit comments

Comments
 (0)