libatomprobe
Library for Atom Probe Tomography (APT) computation
projection.cpp
Go to the documentation of this file.
1 /* projection.cpp : Stereographic and related projection computations
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 "atomprobe/helper/misc.h"
21 
23 
24 #include <cmath>
25 #include <sstream>
26 
27 //GNU Scientific library, for root solving algorithms
28 #include <gsl/gsl_errno.h>
29 #include <gsl/gsl_math.h>
30 #include <gsl/gsl_roots.h>
31 
32 namespace AtomProbe
33 {
34 
35 //return residual of tan(theta) - sin(eta)/(m+cos(eta)) =0
37 {
38  double theta;
39  double focusDist;
40 };
41 
42 double SphericProjectionEqn(double eta, void *p)
43 {
45 
46  return tan(params->theta) - sin(eta)/(params->focusDist+cos(eta));
47 }
48 
49 
50 
51 bool GnomonicProjection::toPlanar(float theta ,float phi,
52  float &fx, float &fy) const
53 {
54  //Only defined for +ve half of sphere
55  if(phi >= M_PI/2.0)
56  return false;
57 
58  //Two equations important here.
59  // 1) sphere equation, in polar coordinates
60  // 2) plane @ z=1
61 
62  //find the point on the sphere surface
63  Point3D pSphere;
64  pSphere.setISOSpherical(1,theta,phi);
65 
66  ASSERT(pSphere[2] >0);
67 
68  //Draw line from centre (0,0,0) of sphere to point (P') on plane
69  // which intersects pSphere
70  // this implies that [P']=t[pSphere], but we know P'_z = 1
71  // so t = 1/pSphere_z
72  float t;
73 
74  t=1.0/pSphere[2];
75 
76  Point3D pPlane=pSphere*t;
77 
78  fx= pPlane[0];
79  fy= pPlane[1];
80 
81  return true;
82 }
83 
84 
85 bool GnomonicProjection::toAzimuthal(float fx, float fy,
86  float &theta, float &phi) const
87 {
88  //theta is formed by the triangle that extends to the plane, with
89  // a radius of r
90  // theta is given by atan2
91  float r= sqrt(fx*fx + fy*fy);
92 
93  theta=atan(r);
94  phi=atan2f(fy,fx);
95 
96  //transform is always valid for any real finite fx,fy pair
97  // but is not unique (0,0)
98  return true;
99 }
100 
101 void GnomonicProjection::scaleDown(float flightLength,
102  float detX,float detY,float &scaledX, float &scaledY) const
103 {
104  ASSERT(flightLength > 0);
105 
106 
107  //similar triangles. scaledRadius/1 = radius/flightLength
108  // 1 due to radius of unit circle
109  scaledX= detX/flightLength;
110  scaledY= detY/flightLength;
111 }
112 
113 void GnomonicProjection::scaleUp(float flightLength,float scaledX,float scaledY,
114  float &realX, float &realY) const
115 {
116  ASSERT(flightLength > 0);
117 
118  //similar triangles. scaledRadius/2 = radius/flightLength
119  // 1 due to radius of unit circle
120  realX=scaledX*flightLength;
121  realY=scaledY*flightLength;
122 }
123 
125  float &fx, float &fy) const
126 {
127  //Multiplied by 2, as this is the triangle length
128  // to the rear of the sphere (from front)
129  float r = 2.0*cos(theta);
130  fx= r*cos(phi);
131  fy= r*sin(phi);
132 
133  return true;
134 }
135 
136 void StereographicProjection::scaleDown(float flightLength,
137  float detX,float detY,float &scaledX, float &scaledY) const
138 {
139  ASSERT(flightLength > 0);
140 
141  //similar triangles. scaledRadius/2 = radius/flightLength
142  // 2 due to diameter of unit circle
143  scaledX= 2.0*detX/flightLength;
144  scaledY= 2.0*detY/flightLength;
145 }
146 
147 void StereographicProjection::scaleUp(float flightLength,float scaledX, float scaledY,
148  float &realX,float &realY) const
149 {
150  ASSERT(flightLength > 0);
151 
152  //similar triangles. scaledRadius/2 = radius/flightLength
153  // 2 due to diameter of unit circle
154  realX = scaledX*flightLength/2.0;
155  realY = scaledY*flightLength/2.0;
156 }
157 
158 bool StereographicProjection::toAzimuthal(float fx, float fy,
159  float &theta, float &phi) const
160 {
161  // phi is given by atan2
162  phi=atan2f(fy,fx);
163 
164  //theta is formed by the triangle that extends to the plane, with
165  // a radius of r, base length 2 (2*unit sphere size)
166  float r= sqrt(fx*fx + fy*fy);
167  theta=atan(r/2.0f);
168 
169  //transform is always valid for any real finite fx,fy pair
170  // but is not unique at (0,0)
171  return true;
172 }
173 
174 
176 {
177  //TODO: Fixme - this is a repeat of MFSP code
178  //actual equation is tan(theta) = sin(eta)/(1+cos(eta))
179 
180  //Use brent/brent-dekker solver on 0,pi to solve
181  const gsl_root_fsolver_type *T = gsl_root_fsolver_brent;
182  gsl_root_fsolver *s = gsl_root_fsolver_alloc (T);
183 
184  gsl_function f;
185  f.function =&SphericProjectionEqn;
187  m.focusDist=1;
188  m.theta=theta;
189  f.params = &m;
190 
191  unsigned int iter=0;
192  double x_lo=0.0;
193  double x_hi=M_PI/2;
194  double r;
195 
196  gsl_root_fsolver_set (s, &f, x_lo, x_hi);
197  int status;
198  do
199  {
200  iter++;
201  gsl_root_fsolver_iterate (s);
202  r = gsl_root_fsolver_root (s);
203  x_lo = gsl_root_fsolver_x_lower (s);
204  x_hi = gsl_root_fsolver_x_upper (s);
205  status = gsl_root_test_interval (x_lo, x_hi,
206  0, 0.001);
207 
208  } while (status == GSL_CONTINUE && iter < 20);
209 
210 
211  gsl_root_fsolver_free(s);
212 
213  if(status != GSL_SUCCESS)
214  return -1.0f;
215 
216  return r;
217 }
218 
220 {
221  setFocus(focus);
222 }
223 
225 {
226  //To be a far side projection, this has to be >1, to lie outside the unit circle,
227  // if < 1, it is inside the circle. Cannot be <0, or will not project to plane
228 // ASSERT(focus>0);
229  focusDist = focus;
230 }
231 
232 
234 {
235  //Eta is the spherical angle, which should be between 0 and PI radiians
236  ASSERT(eta <= M_PI && eta>=0);
237 
238  //the eqn is tan(theta) = sin(eta)/( cos(eta) + m)
239  return atan( sin(eta) / (cos(eta) + focusDist));
240 }
241 
243 {
244 
245  //actual equation is tan(theta) = sin(eta)/(m+cos(eta))
246 
247  //Use brent/brent-dekker solver on 0,pi to solve
248  const gsl_root_fsolver_type *T = gsl_root_fsolver_brent;
249  gsl_root_fsolver *s = gsl_root_fsolver_alloc (T);
250 
251  gsl_function f;
252  f.function =&SphericProjectionEqn;
255  m.theta=theta;
256  f.params = &m;
257 
258  unsigned int iter=0;
259  double x_lo=0.0;
260  double x_hi=M_PI/2;
261  double r;
262 
263  gsl_root_fsolver_set (s, &f, x_lo, x_hi);
264  int status;
265  do
266  {
267  iter++;
268  gsl_root_fsolver_iterate (s);
269  r = gsl_root_fsolver_root (s);
270  x_lo = gsl_root_fsolver_x_lower (s);
271  x_hi = gsl_root_fsolver_x_upper (s);
272  status = gsl_root_test_interval (x_lo, x_hi,
273  0, 0.001);
274 
275  } while (status == GSL_CONTINUE && iter < 20);
276 
277 
278  gsl_root_fsolver_free(s);
279 
280  if(status != GSL_SUCCESS)
281  return -1.0f;
282 
283  return r;
284 }
285 
286 
288 {
289  if(focusDist >1.0)
290  {
291  //Derived from : Line through focus at (-focusDist,0), unit sphere (centered at 0)
292  // and projection line at x=1, tangent to sphere
293  return acos(-1.0f/focusDist);
294  }
295  else
296  {
297  //Focus between 0 and 1
298  //This is the angle formed backwards from the orgin to the focus
299  return acos(-focusDist);
300 
301  }
302 
303 }
304 
306 {
307  //convert to angle at focus point
308  float theta=etaToTheta(eta);
309 
310  return (1+focusDist)*tan(theta);
311 }
312 
313 bool ModifiedFocusSphericProjection::getProjectionPt(float theta, float &f) const
314 {
315  //If does not intersect sphere, then do not allow
316  if(theta >=getMaxFOV())
317  return false;
318 
319  //intersection of line through focus (-focusDist,0)
320  // and projection line at x=1
321  f=(1.0f + focusDist)*tan(theta);
322 
323  return true;
324 }
325 
326 
327 bool ModifiedFocusSphericProjection::toPlanar(float theta, float phi, float &fx, float &fy) const
328 {
329  float fRadius;
330  if(!getProjectionPt(theta,fRadius) )
331  return false;
332 
333 
334  fx = fRadius*cos(phi);
335  fy = fRadius*sin(phi);
336 
337  return true;
338 }
339 
340 
341 bool ModifiedFocusSphericProjection::toAzimuthal(float fx, float fy, float &theta, float &phi) const
342 {
343  float radius;
344 
345  radius=sqrt(fx*fx + fy*fy);
346  theta = atan(radius/(1+focusDist));
347 
348  phi=atan2(fy,fx);
349 
350  if(theta > getMaxFOV())
351  return false;
352 
353  return true;
354 }
355 
357  float detX,float detY,float &scaledX,float &scaledY) const
358 {
359  ASSERT(flightLength > 0);
360 
361  //similar triangles. scaledRadius/2 = radius/flightLength
362  // 2 due to diameter of unit circle
363  scaledX= (1.0+focusDist)*detX/flightLength;
364  scaledY= (1.0+focusDist)*detY/flightLength;
365 }
366 
367 void ModifiedFocusSphericProjection::scaleUp(float flightLength,float scaledX,float scaledY,
368  float &realX,float &realY) const
369 {
370  ASSERT(flightLength > 0);
371 
372  //similar triangles. scaledRadius/2 = radius/flightLength
373  // 2 due to diameter of unit circle
374  realX= scaledX*flightLength/(1.0+focusDist);
375  realY= scaledY*flightLength/(1.0+focusDist);
376 }
377 
378 
379 #ifdef DEBUG
380 
381 bool testAzimuthalProjection();
382 
383 bool testProjection()
384 {
385  if(!testAzimuthalProjection())
386  return false;
387 
388  return true;
389 }
390 
391 bool testAzimuthalProjection()
392 {
393  {
395 
396  //Test projection onto sphere
397  float fx,fy;
398  fx=0.0;
399  fy=1.0;
400 
401  float theta,phi;
402  gp.toAzimuthal(fx,fy,theta,phi);
403  TEST(tolEqual(theta,(float)(45.0f*M_PI/180.0f),0.1f), "toAzimuthal failed");
404 
405  //Test projection from sphere to plane
406  theta=45.0f*M_PI/180.0f;
407  phi=0;
408 
409  fx=fy=0;
410  gp.toPlanar(theta,phi,fx,fy);
411  TEST(tolEqual(fx,1.0f,0.1f),"toPlanar failed (x)");
412  TEST(tolEqual(fy,0.0f,0.1f),"toPlanar failed (y)");
413  }
414 
415  {
417  TEST(tolEqual(mSp1.etaToTheta(0.875129) ,0.43756f,0.01f),"Projection Eqn");
418  }
419 
420  {
422 
423  float theta,phi;
424 
425  mSp.toAzimuthal(3,0,theta,phi);
426 
427  TEST(tolEqual(phi,0.0f,0.1f),"On x axis");
428  TEST(tolEqual(theta,(float)(45.0f*M_PI/180.0f),0.1f),"Should form 1-1-sqrt(2) triangle");
429  TEST(theta < mSp.getMaxFOV(),"Inside FOV");
430 
431  double roundTripTheta;
432  roundTripTheta=mSp.etaToTheta(mSp.thetaToEta(0.1));
433  TEST(tolEqual(roundTripTheta,0.1, 0.01),"Round-trip coordinate transform for modified stereographic");
434  }
435 
436 
437 
438  return true;
439 }
440 
441 
442 
443 #endif
444 }
virtual bool toPlanar(float theta, float phi, float &fx, float &fy) const
Convert from spherical coordinates to plane.
Definition: projection.cpp:51
virtual void scaleDown(float flightLength, float detX, float detY, float &scaledX, float &scaledY) const
Convert from actual detector postion (eg. mm) and flight path to scaled-down transform.
Definition: projection.cpp:101
virtual void scaleUp(float flightLength, float scaledX, float scaledY, float &realX, float &realY) const
Convert from scaled-down radius to actual radius.
Definition: projection.cpp:113
double SphericProjectionEqn(double eta, void *p)
Definition: projection.cpp:42
ModifiedFocusSphericProjection(float focus)
Create a Spheric projection, with a focal point that is moved behind the sphere.
Definition: projection.cpp:219
void setFocus(float focus)
Set the focus position of the sphere. 0= sphere centre, 1=sphere end, >1 is outside sphere...
Definition: projection.cpp:224
#define TEST(f, g)
Definition: aptAssert.h:49
virtual bool toPlanar(float theta, float phi, float &fx, float &fy) const
Convert from stereographic (NOT SPHERICAL) coordinates to plane.
Definition: projection.cpp:124
virtual void scaleUp(float flightLength, float scaledX, float scaledY, float &realX, float &realY) const
Convert from scaled-down to actual dimension.
Definition: projection.cpp:367
A 3D point data class storage.
Definition: point3D.h:39
void setISOSpherical(float radius, float theta, float phi)
Assign the vector using spherical coordinates.
Definition: point3D.cpp:425
virtual bool toAzimuthal(float fx, float fy, float &theta, float &phi) const
Convert from plane coordinates to stereographic (NOT SPHERICAL) coords.
Definition: projection.cpp:341
#define M_PI
Definition: kd-example.cpp:34
virtual bool toPlanar(float theta, float phi, float &fx, float &fy) const
Convert from stereographic (NOT SPHERICAL) coordinates to plane.
Definition: projection.cpp:327
float thetaToEta(float theta) const
Convert azimuthal angle to spherical. See etaToTheta for description.
Definition: projection.cpp:175
float thetaToEta(float theta) const
Convert azimuthal angle to spherical. See etaToTheta for description.
Definition: projection.cpp:242
float etaToTheta(float eta) const
Convert the spherical angle to an azimuthal angle. See docs/figures for examples. ...
Definition: projection.cpp:233
float getMaxFOV() const
Obtain angle spherical angle, eta, (i.e. angle from point of sphere, sphere centre and x axis) of the...
Definition: projection.cpp:287
virtual void scaleDown(float flightLength, float detX, float detY, float &scaledX, float &scaledY) const
Convert from actual detector postion (eg. mm) and flight path to scaled-down transform.
Definition: projection.cpp:356
float getFOVRadius(float eta) const
Obtain the radius in the XY plane of the sphere-angle (eta) that gives us a given FOV...
Definition: projection.cpp:305
#define ASSERT(f)
virtual bool toAzimuthal(float fx, float fy, float &theta, float &phi) const
Convert from plane coordinates to stereographic (NOT SPHERICAL) coords.
Definition: projection.cpp:158
virtual bool toAzimuthal(float fx, float fy, float &theta, float &phi) const
Convert from plane coordinates to spherical coords.
Definition: projection.cpp:85
virtual void scaleUp(float flightLength, float scaledX, float scaledY, float &realX, float &realY) const
Convert from scaled-down radius to actual radius.
Definition: projection.cpp:147
bool tolEqual(const T &a, const T &b, const T &f)
Test for equality within tolerance f (||a-b|| < f)
Definition: misc.h:59
virtual void scaleDown(float flightLength, float detX, float detY, float &scaledX, float &scaledY) const
Convert from actual detector postion (eg. mm) and flight path to scaled-down transform.
Definition: projection.cpp:136