20
20
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
21
21
*/
22
22
23
+ using BH . oM . Base ;
23
24
using BH . oM . Base . Attributes ;
24
25
using BH . oM . Geometry ;
25
- using System ;
26
26
using System . Collections . Generic ;
27
27
using System . ComponentModel ;
28
28
using System . Linq ;
@@ -32,7 +32,7 @@ namespace BH.Engine.Geometry
32
32
public static partial class Compute
33
33
{
34
34
/***************************************************/
35
- /**** public Methods - Vectors ****/
35
+ /**** Public Methods ****/
36
36
/***************************************************/
37
37
38
38
[ 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
46
46
if ( n < 2 )
47
47
return null ;
48
48
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
51
49
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 ++ )
58
53
{
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 ;
70
57
}
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 ;
103
58
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 } ;
135
63
}
136
64
137
65
/***************************************************/
0 commit comments