diff 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
line wrap: on
line diff
--- a/OrthancStone/Sources/Toolbox/CoordinateSystem3D.cpp	Thu Nov 12 16:57:15 2020 +0100
+++ b/OrthancStone/Sources/Toolbox/CoordinateSystem3D.cpp	Thu Nov 12 19:27:08 2020 +0100
@@ -29,6 +29,8 @@
 #include <Toolbox.h>
 #include <OrthancException.h>
 
+#include <boost/math/constants/constants.hpp>
+
 namespace OrthancStone
 {
   void CoordinateSystem3D::CheckAndComputeNormal()
@@ -61,7 +63,7 @@
 
       d_ = -(normal_[0] * origin_[0] + normal_[1] * origin_[1] + normal_[2] * origin_[2]);
 
-      // Just a sanity check, it should be useless by construction
+      // Just a sanity check, it should be useless by construction (*)
       assert(LinearAlgebra::IsNear(boost::numeric::ublas::norm_2(normal_), 1.0));
     }
   }
@@ -229,6 +231,18 @@
   }
 
 
+  double CoordinateSystem3D::ComputeDistance(const Vector& p) const
+  {
+    /**
+     * "normal_" is an unit vector (*) => sqrt(a_1^2+a_2^2+a_3^2) = 1,
+     * and the denominator equals 1 by construction.
+     * https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_plane#Closest_point_and_distance_for_a_hyperplane_and_arbitrary_point
+     **/
+
+    return std::abs(boost::numeric::ublas::inner_prod(p, normal_) + d_);
+  }
+
+
   bool CoordinateSystem3D::ComputeDistance(double& distance,
                                            const CoordinateSystem3D& a,
                                            const CoordinateSystem3D& b)
@@ -265,4 +279,81 @@
     normalized.SetOrigin(plane.MapSliceToWorldCoordinates(ox, oy));
     return normalized;
   }
+
+
+  CoordinateSystem3D CoordinateSystem3D::CreateFromPlaneGeneralForm(double a,
+                                                                    double b,
+                                                                    double c,
+                                                                    double d)
+  {
+    /**
+     * "a*x + b*y + c*z + d = 0" => The un-normalized normal is vector
+     * (a,b,c). 
+     **/
+
+    Vector normal;
+    LinearAlgebra::AssignVector(normal, a, b, c);
+
+    
+    /**
+     * Choose the origin of plane, as the point that is closest to the
+     * origin of the axes (0,0,0). 
+     * https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_plane#Restatement_using_linear_algebra
+     **/
+
+    double squaredNorm = a * a + b * b + c * c;
+    if (LinearAlgebra::IsCloseToZero(squaredNorm))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry, "Singular matrix");
+    }        
+    
+    Vector origin = -d * normal / squaredNorm;
+
+
+    /**
+     * Select the X axis by computing a vector that is perpendicular
+     * to the normal.
+     *
+     * "Exactly 1 and only 1 of the bools get set; b0/b1/b2 gets set
+     * if dimension "i" has magnitude strictly less than all
+     * subsequent dimensions and not greater than all previous
+     * dimensions. We then have a unit vector with a single non-zero
+     * dimension that corresponds to a dimension of minimum magnitude
+     * in "normal". The cross product of this with "normal" is
+     * orthogonal to "normal" by definition of cross product. Consider
+     * now that the cross product is numerically unstable only when
+     * the two vectors are very closely aligned. Consider that our
+     * unit vector is large in only a single dimension and that that
+     * dimension corresponds to the dimension where "normal" was
+     * small. It's thus guaranteed to be loosely orthogonal to
+     * "normal" before taking the cross product, with least
+     * orthogonality in the case where all dimensions of "normal" are
+     * equal. In this least-orthogonal case, we're still quite
+     * orthogonal given that our unit vector has all but one dimension
+     * 0 whereas "normal" has all equal. We thus avoid the unstable
+     * case of taking the cross product of two nearly-aligned
+     * vectors."  https://stackoverflow.com/a/43454629/881731
+     **/
+
+    bool b0 = (normal[0] <  normal[1]) && (normal[0] <  normal[2]);
+    bool b1 = (normal[1] <= normal[0]) && (normal[1] <  normal[2]);
+    bool b2 = (normal[2] <= normal[0]) && (normal[2] <= normal[1]);
+    Vector swap = LinearAlgebra::CreateVector(b0 ? 1 : 0, b1 ? 1 : 0, b2 ? 1 : 0);
+    
+    Vector axisX;
+    LinearAlgebra::CrossProduct(axisX, normal, swap);
+    LinearAlgebra::NormalizeVector(axisX);
+
+    
+    /**
+     * The Y axis follows as the cross-product of the normal and the X
+     * axis.
+     **/
+    
+    Vector axisY;
+    LinearAlgebra::CrossProduct(axisY, axisX, normal);
+    LinearAlgebra::NormalizeVector(axisY);
+
+    return CoordinateSystem3D(origin, axisX, axisY);
+  }
 }