libatomprobe
Library for Atom Probe Tomography (APT) computation
misc.cpp
Go to the documentation of this file.
1 /* misc.cpp : various numerical functions
2  * Copyright (C) 2020 Daniel Haley
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
19 
20 #include "gsl/gsl_linalg.h"
22 namespace AtomProbe
23 {
24 void gsl_matrix_mult(const gsl_matrix *A, const gsl_matrix *B, gsl_matrix* &res,
25  bool alloc)
26 {
27  //Check matrices are correctly sized, and allocate as needed
28  ASSERT(B->size1 ==A->size2);
29  if(alloc)
30  {
31  res = gsl_matrix_alloc(A->size1,B->size2);
32  }
33  else
34  {
35  ASSERT(res->size1 == A->size1 &&
36  res->size2 == B->size2);
37  }
38 
39  //Naïve matrix mult
40  for(unsigned int ui=0; ui<A->size1; ui++)
41  {
42  for(unsigned int uj=0;uj<B->size2; uj++)
43  {
44  double cuml;
45  cuml=0.0;
46  for(unsigned int uk=0;uk<B->size1; uk++)
47  {
48  cuml+= gsl_matrix_get(A,ui,uk)
49  *gsl_matrix_get(B,uk,uj);
50  }
51 
52  gsl_matrix_set(res, ui,uj,cuml);
53  }
54  }
55 }
56 
57 
58 unsigned int estimateRank(const gsl_matrix *m, float tolerance)
59 {
60 
61  auto w = gsl_vector_alloc(m->size2);
62  auto s = gsl_vector_alloc(m->size2);
63 
64  auto v = gsl_matrix_alloc(m->size2,m->size2);
65 
66  auto m2 = gsl_matrix_alloc(m->size1,m->size2);
67  gsl_matrix_memcpy(m2,m);
68  if(gsl_linalg_SV_decomp(m2,v,s,w))
69  {
70  gsl_vector_free(w);
71  gsl_vector_free(s);
72  gsl_matrix_free(v);
73 
74  gsl_matrix_free(m2);
75  return -1;
76  }
77  unsigned int rank=0;
78  for(auto ui=0;ui<s->size; ui++)
79  {
80  if(gsl_vector_get(s,ui) > tolerance)
81  rank=ui+1;
82  else
83  break;
84  }
85 
86  gsl_vector_free(w);
87  gsl_vector_free(s);
88  gsl_matrix_free(v);
89  gsl_matrix_free(m2);
90 
91  return rank;
92 }
93 
94 bool solveLeastSquares(gsl_matrix *m, gsl_vector *b, gsl_vector* &x)
95 {
96  auto w = gsl_vector_alloc(m->size2);
97  auto s = gsl_vector_alloc(m->size2);
98 
99  auto v = gsl_matrix_alloc(m->size2,m->size2);
100 
101  if(gsl_linalg_SV_decomp(m,v,s,w))
102  {
103  gsl_vector_free(w);
104  gsl_vector_free(s);
105  gsl_matrix_free(v);
106  return false;
107  }
108 
109 
110  if(gsl_linalg_SV_solve(m, v, s, b, x))
111  {
112  gsl_vector_free(x);
113  gsl_vector_free(w);
114  gsl_vector_free(s);
115  gsl_matrix_free(v);
116  return false;
117  }
118 
119  gsl_vector_free(w);
120  gsl_vector_free(s);
121  gsl_matrix_free(v);
122 
123 
124  return true;
125 }
126 
127 unsigned int vectorPointDir(const Point3D &pA, const Point3D &pB,
128  const Point3D &vC, const Point3D &vD)
129 {
130  //Check which way vectors attached to two 3D points "point",
131  // - "together", "apart" or "in common"
132 
133  //calculate AB.CA, BA.DB
134  float dot1 = (pB-pA).dotProd(vC - pA);
135  float dot2= (pA - pB).dotProd(vD - pB);
136 
137  //We shall somehwat arbitrarily call perpendicular cases "together"
138  if(dot1 ==0.0f || dot2 == 0.0f)
139  return POINTDIR_TOGETHER;
140 
141  //If they have opposite signs, then they are "in common"
142  if(( dot1 < 0.0f && dot2 > 0.0f) || (dot1 > 0.0f && dot2 < 0.0f) )
143  return POINTDIR_IN_COMMON;
144 
145  if( dot1 < 0.0f && dot2 <0.0f )
146  return POINTDIR_APART;
147 
148  if(dot1 > 0.0f && dot2 > 0.0f )
149  return POINTDIR_TOGETHER;
150 
151  ASSERT(false)
152  return 0;
153 }
154 
155 //Distance between a line segment and a point in 3D space
156 float distanceToSegment(const Point3D &fA, const Point3D &fB, const Point3D &p)
157 {
158 
159  //If the vectors ar pointing "together" then use point-line formula
160  if(vectorPointDir(fA,fB,p,p) == POINTDIR_TOGETHER)
161  {
162  Point3D closestPt;
163  Point3D vAB= fB-fA;
164 
165  //Use formula d^2 = |(B-A)(cross)(A-P)|^2/|B-A|^2
166  return sqrt( (vAB.crossProd(fA-p)).sqrMag()/(vAB.sqrMag()));
167  }
168 
169  return sqrt( std::min(fB.sqrDist(p), fA.sqrDist(p)) );
170 }
171 
172 float signedDistanceToFacet(const Point3D &fA, const Point3D &fB,
173  const Point3D &fC, const Point3D &normal,const Point3D &p)
174 {
175 
176  Point3D pTri[3];
177  pTri[0]=fA;
178  pTri[1]=fB;
179  pTri[2]=fC;
180 
181  bool insideTri;
182 
183  float baryCentricCoord[3];
184  for(size_t ui=0; ui<3;ui++)
185  {
186  //Find the midpoint of the triangle on this axis
187  Point3D mid,tmp;
188  mid = (pTri[(ui+1)%3] -pTri[ui])*0.5f + pTri[ui];
189  //Vector between current "apex" and midpoint of opposite side
190  tmp = (pTri[(ui+2)%3]-mid);
191 
192  baryCentricCoord[ui] = tmp.dotProd(p -mid)/(tmp.mag());
193  }
194 
195  insideTri=true;
196  for(size_t ui=0;ui<3;ui++)
197  {
198  insideTri&= (0.0f <=baryCentricCoord[ui] &&
199  baryCentricCoord[ui] <=1.0f);
200  }
201 
202 
203  //Check to see if any of them are "in common"
204  if(!insideTri)
205  {
206  //if so, we have to check each edge for its closest point
207  //then pick the best
208  float bestDist[3];
209  bestDist[0] = distanceToSegment(fA,fB,p);
210  bestDist[1] = distanceToSegment(fA,fC,p);
211  bestDist[2] = distanceToSegment(fB,fC,p);
212 
213 
214  float minV=std::numeric_limits<float>::max();
215  size_t offset;
216  for(size_t ui=0;ui<3;ui++)
217  {
218  if( bestDist[ui] < minV)
219  {
220  offset=ui;
221  minV=bestDist[ui];
222  }
223  }
224 
225  //Check which side of the triangle it sits on
226  // using one of the vertices in the plane of the triangle
227  //(any, doesn't matter which (ign. fp..) )
228  float sign;
229  sign =normal.dotProd(p-fA);
230  if(sign > 0)
231  return bestDist[offset];
232  else
233  return -bestDist[offset];
234  }
235 
236  float temp;
237 
238  temp = (p-fA).dotProd(normal);
239 
240  //check that the other points were not better than this!
241  ASSERT(sqrt(fA.sqrDist(p)) >= fabs(temp) - sqrt(std::numeric_limits<float>::epsilon()));
242  ASSERT(sqrt(fB.sqrDist(p)) >= fabs(temp) - sqrt(std::numeric_limits<float>::epsilon()));
243  ASSERT(sqrt(fC.sqrDist(p)) >= fabs(temp) - sqrt(std::numeric_limits<float>::epsilon()));
244 
245  //Point lies above/below facet, use plane formula
246  return temp;
247 }
248 
249 
250 float distanceToFacet(const Point3D &fA, const Point3D &fB,
251  const Point3D &fC, const Point3D &normal,const Point3D &p)
252 {
253  return fabs(signedDistanceToFacet(fA,fB,fC,normal,p));
254 }
255 
256 double det3by3(const double *ptArray)
257 {
258  return (ptArray[0]*(ptArray[4]*ptArray[8]
259  - ptArray[7]*ptArray[5])
260  - ptArray[1]*(ptArray[3]*ptArray[8]
261  - ptArray[6]*ptArray[5])
262  + ptArray[2]*(ptArray[3]*ptArray[7]
263  - ptArray[4]*ptArray[6]));
264 }
265 
266 bool triIsDegenerate(const Point3D &fA, const Point3D &fB,
267  const Point3D &fC)
268 {
269  Point3D pTri[3];
270  pTri[0]=fA;
271  pTri[1]=fB;
272  pTri[2]=fC;
273 
274  for(size_t ui=0; ui<3;ui++)
275  {
276  //Find the midpoint of the triangle on this axis
277  Point3D mid,tmp;
278  mid = (pTri[(ui+1)%3] -pTri[ui])*0.5f + pTri[ui];
279  //Vector between current "apex" and midpoint of opposite side
280  tmp = (pTri[(ui+2)%3]-mid);
281 
282  if(tmp.mag() < sqrt(std::numeric_limits<float>::epsilon()))
283  return true;
284 
285  }
286 
287  return false;
288 }
289 
290 double pyramidVol(const Point3D *planarPts, const Point3D &apex)
291 {
292 
293  //Array for 3D simplex volumed determination
294  // | (a_x - b_x) (b_x - c_x) (c_x - d_x) |
295  //v_simplex =1/6| (a_y - b_y) (b_y - c_y) (c_y - d_y) |
296  // | (a_z - b_z) (b_z - c_z) (c_z - d_z) |
297  double simplexA[9];
298 
299  //simplex A (a,b,c,apex) is as follows
300  //a=planarPts[0] b=planarPts[1] c=planarPts[2]
301 
302  simplexA[0] = (double)( (planarPts[0])[0] - (planarPts[1])[0] );
303  simplexA[1] = (double)( (planarPts[1])[0] - (planarPts[2])[0] );
304  simplexA[2] = (double)( (planarPts[2])[0] - (apex)[0] );
305 
306  simplexA[3] = (double)( (planarPts[0])[1] - (planarPts[1])[1] );
307  simplexA[4] = (double)( (planarPts[1])[1] - (planarPts[2])[1] );
308  simplexA[5] = (double)( (planarPts[2])[1] - (apex)[1] );
309 
310  simplexA[6] = (double)( (planarPts[0])[2] - (planarPts[1])[2] );
311  simplexA[7] = (double)( (planarPts[1])[2] - (planarPts[2])[2] );
312  simplexA[8] = (double)( (planarPts[2])[2] - (apex)[2] );
313 
314  return 1.0/6.0 * (fabs(det3by3(simplexA)));
315 }
316 #ifdef DEBUG
317 
318 bool runMiscMathsTests()
319 {
320  for(auto ui=1;ui<10;ui++)
321  {
322  gsl_matrix *m;
323  m= gsl_matrix_alloc(ui,ui);
324 
325  gsl_matrix_set_identity(m);
326 
327  ASSERT(estimateRank(m) ==ui);
328  gsl_matrix_free(m);
329  }
330 
331 
332  auto *m = gsl_matrix_alloc(2,2);
333  gsl_matrix_set_zero(m);
334  ASSERT(estimateRank(m) ==0);
335  gsl_matrix_set(m,1,1,0.5);
336  ASSERT(estimateRank(m) ==1);
337  gsl_matrix_free(m);
338 
339 
340  return true;
341 }
342 #endif
343 }
float sqrDist(const Point3D &pt) const
Returns the square of distance to another Point3D.
Definition: point3D.cpp:272
float signedDistanceToFacet(const Point3D &fA, const Point3D &fB, const Point3D &fC, const Point3D &normal, const Point3D &p)
Find the distance between a point, and a triangular facet – may be positive or negative.
Definition: misc.cpp:172
double pyramidVol(const Point3D *planarPts, const Point3D &apex)
Definition: misc.cpp:290
Point3D crossProd(const Point3D &pt) const
Calculate the cross product of this and another point.
Definition: point3D.cpp:285
void gsl_matrix_mult(const gsl_matrix *A, const gsl_matrix *B, gsl_matrix *&res, bool alloc=false)
Definition: misc.cpp:24
bool solveLeastSquares(gsl_matrix *m, gsl_vector *b, gsl_vector *&x)
Use an SVD based least-squares solver to solve Mx=b (for x).
Definition: misc.cpp:94
float distanceToSegment(const Point3D &fA, const Point3D &fB, const Point3D &p)
Definition: misc.cpp:156
float sqrMag() const
Returns magnitude^2, taking this as a position vector.
Definition: point3D.cpp:332
A 3D point data class storage.
Definition: point3D.h:39
float mag() const
Magnitude of position vector.
Definition: point3D.h:197
double det3by3(const double *ptArray)
Definition: misc.cpp:256
#define ASSERT(f)
unsigned int estimateRank(const gsl_matrix *m, float tolerance=sqrt(std::numeric_limits< float >::epsilon()))
Estimate the rank of the given matrix.
Definition: misc.cpp:58
bool triIsDegenerate(const Point3D &fA, const Point3D &fB, const Point3D &fC)
Definition: misc.cpp:266
float dotProd(const Point3D &pt) const
Calculate the dot product of this and another point.
Definition: point3D.cpp:279
float distanceToFacet(const Point3D &fA, const Point3D &fB, const Point3D &fC, const Point3D &normal, const Point3D &p)
Definition: misc.cpp:250
unsigned int vectorPointDir(const Point3D &pA, const Point3D &pB, const Point3D &vC, const Point3D &vD)
Check which way vectors attached to two 3D points "point",.
Definition: misc.cpp:127