comparison OrthancStone/Sources/Toolbox/CoordinateSystem3D.cpp @ 1648:4a43106bc122

cross-hair starts to work
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 12 Nov 2020 19:27:08 +0100
parents 52b8b96cb55f
children af312e145980
comparison
equal deleted inserted replaced
1647:adff3cd78775 1648:4a43106bc122
27 27
28 #include <Logging.h> 28 #include <Logging.h>
29 #include <Toolbox.h> 29 #include <Toolbox.h>
30 #include <OrthancException.h> 30 #include <OrthancException.h>
31 31
32 #include <boost/math/constants/constants.hpp>
33
32 namespace OrthancStone 34 namespace OrthancStone
33 { 35 {
34 void CoordinateSystem3D::CheckAndComputeNormal() 36 void CoordinateSystem3D::CheckAndComputeNormal()
35 { 37 {
36 // DICOM expects normal vectors to define the axes: "The row and 38 // DICOM expects normal vectors to define the axes: "The row and
59 { 61 {
60 LinearAlgebra::CrossProduct(normal_, axisX_, axisY_); 62 LinearAlgebra::CrossProduct(normal_, axisX_, axisY_);
61 63
62 d_ = -(normal_[0] * origin_[0] + normal_[1] * origin_[1] + normal_[2] * origin_[2]); 64 d_ = -(normal_[0] * origin_[0] + normal_[1] * origin_[1] + normal_[2] * origin_[2]);
63 65
64 // Just a sanity check, it should be useless by construction 66 // Just a sanity check, it should be useless by construction (*)
65 assert(LinearAlgebra::IsNear(boost::numeric::ublas::norm_2(normal_), 1.0)); 67 assert(LinearAlgebra::IsNear(boost::numeric::ublas::norm_2(normal_), 1.0));
66 } 68 }
67 } 69 }
68 70
69 71
227 { 229 {
228 return GeometryToolbox::IntersectPlaneAndLine(p, normal_, d_, origin, direction); 230 return GeometryToolbox::IntersectPlaneAndLine(p, normal_, d_, origin, direction);
229 } 231 }
230 232
231 233
234 double CoordinateSystem3D::ComputeDistance(const Vector& p) const
235 {
236 /**
237 * "normal_" is an unit vector (*) => sqrt(a_1^2+a_2^2+a_3^2) = 1,
238 * and the denominator equals 1 by construction.
239 * https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_plane#Closest_point_and_distance_for_a_hyperplane_and_arbitrary_point
240 **/
241
242 return std::abs(boost::numeric::ublas::inner_prod(p, normal_) + d_);
243 }
244
245
232 bool CoordinateSystem3D::ComputeDistance(double& distance, 246 bool CoordinateSystem3D::ComputeDistance(double& distance,
233 const CoordinateSystem3D& a, 247 const CoordinateSystem3D& a,
234 const CoordinateSystem3D& b) 248 const CoordinateSystem3D& b)
235 { 249 {
236 bool opposite = false; // Ignored 250 bool opposite = false; // Ignored
263 277
264 CoordinateSystem3D normalized(plane); 278 CoordinateSystem3D normalized(plane);
265 normalized.SetOrigin(plane.MapSliceToWorldCoordinates(ox, oy)); 279 normalized.SetOrigin(plane.MapSliceToWorldCoordinates(ox, oy));
266 return normalized; 280 return normalized;
267 } 281 }
282
283
284 CoordinateSystem3D CoordinateSystem3D::CreateFromPlaneGeneralForm(double a,
285 double b,
286 double c,
287 double d)
288 {
289 /**
290 * "a*x + b*y + c*z + d = 0" => The un-normalized normal is vector
291 * (a,b,c).
292 **/
293
294 Vector normal;
295 LinearAlgebra::AssignVector(normal, a, b, c);
296
297
298 /**
299 * Choose the origin of plane, as the point that is closest to the
300 * origin of the axes (0,0,0).
301 * https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_plane#Restatement_using_linear_algebra
302 **/
303
304 double squaredNorm = a * a + b * b + c * c;
305 if (LinearAlgebra::IsCloseToZero(squaredNorm))
306 {
307 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, "Singular matrix");
308 }
309
310 Vector origin = -d * normal / squaredNorm;
311
312
313 /**
314 * Select the X axis by computing a vector that is perpendicular
315 * to the normal.
316 *
317 * "Exactly 1 and only 1 of the bools get set; b0/b1/b2 gets set
318 * if dimension "i" has magnitude strictly less than all
319 * subsequent dimensions and not greater than all previous
320 * dimensions. We then have a unit vector with a single non-zero
321 * dimension that corresponds to a dimension of minimum magnitude
322 * in "normal". The cross product of this with "normal" is
323 * orthogonal to "normal" by definition of cross product. Consider
324 * now that the cross product is numerically unstable only when
325 * the two vectors are very closely aligned. Consider that our
326 * unit vector is large in only a single dimension and that that
327 * dimension corresponds to the dimension where "normal" was
328 * small. It's thus guaranteed to be loosely orthogonal to
329 * "normal" before taking the cross product, with least
330 * orthogonality in the case where all dimensions of "normal" are
331 * equal. In this least-orthogonal case, we're still quite
332 * orthogonal given that our unit vector has all but one dimension
333 * 0 whereas "normal" has all equal. We thus avoid the unstable
334 * case of taking the cross product of two nearly-aligned
335 * vectors." https://stackoverflow.com/a/43454629/881731
336 **/
337
338 bool b0 = (normal[0] < normal[1]) && (normal[0] < normal[2]);
339 bool b1 = (normal[1] <= normal[0]) && (normal[1] < normal[2]);
340 bool b2 = (normal[2] <= normal[0]) && (normal[2] <= normal[1]);
341 Vector swap = LinearAlgebra::CreateVector(b0 ? 1 : 0, b1 ? 1 : 0, b2 ? 1 : 0);
342
343 Vector axisX;
344 LinearAlgebra::CrossProduct(axisX, normal, swap);
345 LinearAlgebra::NormalizeVector(axisX);
346
347
348 /**
349 * The Y axis follows as the cross-product of the normal and the X
350 * axis.
351 **/
352
353 Vector axisY;
354 LinearAlgebra::CrossProduct(axisY, axisX, normal);
355 LinearAlgebra::NormalizeVector(axisY);
356
357 return CoordinateSystem3D(origin, axisX, axisY);
358 }
268 } 359 }