From 1bb5b4a74e89eb501e288a238d116abf4e4b2388 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (h)" <j.wuttke@fz-juelich.de>
Date: Sat, 28 May 2022 22:12:07 +0200
Subject: [PATCH] provide and use RotMatrix::angleAroundCoordAxis

---
 Base/Vector/RotMatrix.cpp           | 11 ++++
 Base/Vector/RotMatrix.h             |  3 ++
 Sample/Scattering/Rotations.cpp     | 34 +++++-------
 Sim/Export/SampleToPython.cpp       | 47 ++++++++---------
 auto/Wrap/doxygenBase.i             |  3 ++
 auto/Wrap/libBornAgainBase.py       |  8 +++
 auto/Wrap/libBornAgainBase_wrap.cpp | 80 +++++++++++++++++++++--------
 7 files changed, 117 insertions(+), 69 deletions(-)

diff --git a/Base/Vector/RotMatrix.cpp b/Base/Vector/RotMatrix.cpp
index 2dd4f744f35..bfd9dbf02c2 100644
--- a/Base/Vector/RotMatrix.cpp
+++ b/Base/Vector/RotMatrix.cpp
@@ -163,3 +163,14 @@ bool RotMatrix::isZRotation() const
 {
     return x == 0 && y == 0;
 }
+
+std::optional<double> RotMatrix::angleAroundCoordAxis(int iAxis) const
+{
+    if (iAxis==0 && y == 0 && z == 0)
+        return 2 * atan2(x, s);
+    if (iAxis==1 && x == 0 && z == 0)
+        return 2 * atan2(y, s);
+    if (iAxis==2 && x == 0 && y == 0)
+        return 2 * atan2(z, s);
+    return {};
+}
diff --git a/Base/Vector/RotMatrix.h b/Base/Vector/RotMatrix.h
index 2e44b75175a..dc8f5e255e5 100644
--- a/Base/Vector/RotMatrix.h
+++ b/Base/Vector/RotMatrix.h
@@ -17,6 +17,7 @@
 
 #include <heinz/Vectors3D.h>
 #include <array>
+#include <optional>
 
 //! Rotation matrix in three dimensions.
 
@@ -87,6 +88,8 @@ public:
     bool isYRotation() const;
     bool isZRotation() const;
 
+    std::optional<double> angleAroundCoordAxis(int iAxis) const;
+
 private:
     RotMatrix(double x_, double y_, double z_, double s_);
 
diff --git a/Sample/Scattering/Rotations.cpp b/Sample/Scattering/Rotations.cpp
index 8f60252e12c..e584ce4af4f 100644
--- a/Sample/Scattering/Rotations.cpp
+++ b/Sample/Scattering/Rotations.cpp
@@ -25,28 +25,18 @@ IRotation::IRotation(const std::vector<double>& PValues)
 {
 }
 
-IRotation* IRotation::createRotation(const RotMatrix& transform)
-{
-    auto rot_type = transform.getRotationType();
-    switch (rot_type) {
-    case RotMatrix::XAXIS: {
-        double angle = transform.calculateRotateXAngle();
-        return new RotationX(angle);
-    }
-    case RotMatrix::YAXIS: {
-        double angle = transform.calculateRotateYAngle();
-        return new RotationY(angle);
-    }
-    case RotMatrix::ZAXIS: {
-        double angle = transform.calculateRotateZAngle();
-        return new RotationZ(angle);
-    }
-    case RotMatrix::EULER: {
-        auto angles = transform.zxzEulerAngles();
-        return new RotationEuler(angles[0], angles[1], angles[2]);
-    }
-    }
-    ASSERT(0); // impossible case
+IRotation* IRotation::createRotation(const RotMatrix& matrix)
+{
+    if (matrix.isIdentity())
+        return new IdentityRotation;
+    if (std::optional<double> angle = matrix.angleAroundCoordAxis(0))
+        return new RotationX(angle.value());
+    if (std::optional<double> angle = matrix.angleAroundCoordAxis(1))
+        return new RotationY(angle.value());
+    if (std::optional<double> angle = matrix.angleAroundCoordAxis(2))
+        return new RotationZ(angle.value());
+    auto angles = matrix.zxzEulerAngles();
+    return new RotationEuler(angles[0], angles[1], angles[2]);
 }
 
 R3 IRotation::transformed(const R3& v) const
diff --git a/Sim/Export/SampleToPython.cpp b/Sim/Export/SampleToPython.cpp
index cf492590ec6..3947633e652 100644
--- a/Sim/Export/SampleToPython.cpp
+++ b/Sim/Export/SampleToPython.cpp
@@ -39,6 +39,8 @@
 
 using Py::Fmt::indent;
 
+static const std::map<int, char> axisChar {{0,'X'}, {1,'Y'}, {2,'Z'}};
+
 namespace {
 
 void setRotationInformation(const IParticle* particle, std::string name, std::ostringstream& result)
@@ -46,34 +48,25 @@ void setRotationInformation(const IParticle* particle, std::string name, std::os
     if (!particle->rotation())
         return;
     const RotMatrix matrix = particle->rotation()->rotMatrix();
-    switch (matrix.getRotationType()) {
-    case RotMatrix::EULER: {
-        auto angles = matrix.zxzEulerAngles();
-        result << indent() << name << "_rotation = ba.RotationEuler("
-               << Py::Fmt::printDegrees(angles[0]) << ", "
-               << Py::Fmt::printDegrees(angles[1]) << ", "
-               << Py::Fmt::printDegrees(angles[2]) << ")\n";
-        break;
-    }
-    case RotMatrix::XAXIS: {
-        double alpha = matrix.calculateRotateXAngle();
-        result << indent() << name << "_rotation = ba.RotationX("
-               << Py::Fmt::printDegrees(alpha) << ")\n";
-        break;
-    }
-    case RotMatrix::YAXIS: {
-        double alpha = matrix.calculateRotateYAngle();
-        result << indent() << name << "_rotation = ba.RotationY("
-               << Py::Fmt::printDegrees(alpha) << ")\n";
-        break;
-    }
-    case RotMatrix::ZAXIS: {
-        double alpha = matrix.calculateRotateZAngle();
-        result << indent() << name << "_rotation = ba.RotationZ("
-               << Py::Fmt::printDegrees(alpha) << ")\n";
-        break;
-    }
+    // Identity matrix?
+    if (matrix.isIdentity())
+        return;
+    // Rotation around coordinate axis?
+    for (int iAxis = 0; iAxis<3; ++iAxis) {
+        std::optional<double> angle = matrix.angleAroundCoordAxis(iAxis);
+        if (angle) {
+            result << indent() << name << "_rotation = ba.Rotation" << axisChar.at(iAxis) << "("
+                   << Py::Fmt::printDegrees(angle.value()) << ")\n";
+            result << indent() << name << ".setRotation(" << name << "_rotation)\n";
+            return;
+        }
     }
+    // Generic rotation.
+    auto angles = matrix.zxzEulerAngles();
+    result << indent() << name << "_rotation = ba.RotationEuler("
+           << Py::Fmt::printDegrees(angles[0]) << ", "
+           << Py::Fmt::printDegrees(angles[1]) << ", "
+           << Py::Fmt::printDegrees(angles[2]) << ")\n";
     result << indent() << name << ".setRotation(" << name << "_rotation)\n";
 }
 
diff --git a/auto/Wrap/doxygenBase.i b/auto/Wrap/doxygenBase.i
index b7b0d2443a9..6fa6bd525c4 100644
--- a/auto/Wrap/doxygenBase.i
+++ b/auto/Wrap/doxygenBase.i
@@ -1067,6 +1067,9 @@ Determine if the transformation is trivial (identity)
 %feature("docstring")  RotMatrix::isZRotation "bool RotMatrix::isZRotation() const
 ";
 
+%feature("docstring")  RotMatrix::angleAroundCoordAxis "std::optional< double > RotMatrix::angleAroundCoordAxis(int iAxis) const
+";
+
 
 // File: classSphericalPixel.xml
 %feature("docstring") SphericalPixel "
diff --git a/auto/Wrap/libBornAgainBase.py b/auto/Wrap/libBornAgainBase.py
index 90136aa54d0..1ce6b0abdd3 100644
--- a/auto/Wrap/libBornAgainBase.py
+++ b/auto/Wrap/libBornAgainBase.py
@@ -2038,6 +2038,14 @@ class RotMatrix(object):
         """
         return _libBornAgainBase.RotMatrix_isZRotation(self)
 
+    def angleAroundCoordAxis(self, iAxis):
+        r"""
+        angleAroundCoordAxis(RotMatrix self, int iAxis) -> std::optional< double >
+        std::optional< double > RotMatrix::angleAroundCoordAxis(int iAxis) const
+
+        """
+        return _libBornAgainBase.RotMatrix_angleAroundCoordAxis(self, iAxis)
+
 # Register RotMatrix in _libBornAgainBase:
 _libBornAgainBase.RotMatrix_swigregister(RotMatrix)
 
diff --git a/auto/Wrap/libBornAgainBase_wrap.cpp b/auto/Wrap/libBornAgainBase_wrap.cpp
index 87c9cc88a22..eff906328eb 100644
--- a/auto/Wrap/libBornAgainBase_wrap.cpp
+++ b/auto/Wrap/libBornAgainBase_wrap.cpp
@@ -3139,26 +3139,27 @@ namespace Swig {
 #define SWIGTYPE_p_std__invalid_argument swig_types[39]
 #define SWIGTYPE_p_std__lessT_std__string_t swig_types[40]
 #define SWIGTYPE_p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t swig_types[41]
-#define SWIGTYPE_p_std__pairT_double_double_t swig_types[42]
-#define SWIGTYPE_p_std__vectorT_IAxis_const_p_std__allocatorT_IAxis_const_p_t_t swig_types[43]
-#define SWIGTYPE_p_std__vectorT_IAxis_p_std__allocatorT_IAxis_p_t_t swig_types[44]
-#define SWIGTYPE_p_std__vectorT_double_std__allocatorT_double_t_t swig_types[45]
-#define SWIGTYPE_p_std__vectorT_int_std__allocatorT_int_t_t swig_types[46]
-#define SWIGTYPE_p_std__vectorT_std__complexT_double_t_std__allocatorT_std__complexT_double_t_t_t swig_types[47]
-#define SWIGTYPE_p_std__vectorT_std__pairT_double_double_t_std__allocatorT_std__pairT_double_double_t_t_t swig_types[48]
-#define SWIGTYPE_p_std__vectorT_std__string_std__allocatorT_std__string_t_t swig_types[49]
-#define SWIGTYPE_p_std__vectorT_std__vectorT_double_std__allocatorT_double_t_t_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t_t swig_types[50]
-#define SWIGTYPE_p_std__vectorT_std__vectorT_int_std__allocatorT_int_t_t_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t_t swig_types[51]
-#define SWIGTYPE_p_std__vectorT_unsigned_int_std__allocatorT_unsigned_int_t_t swig_types[52]
-#define SWIGTYPE_p_std__vectorT_unsigned_long_std__allocatorT_unsigned_long_t_t swig_types[53]
-#define SWIGTYPE_p_swig__SwigPyIterator swig_types[54]
-#define SWIGTYPE_p_unsigned_char swig_types[55]
-#define SWIGTYPE_p_unsigned_int swig_types[56]
-#define SWIGTYPE_p_unsigned_long_long swig_types[57]
-#define SWIGTYPE_p_unsigned_short swig_types[58]
-#define SWIGTYPE_p_value_type swig_types[59]
-static swig_type_info *swig_types[61];
-static swig_module_info swig_module = {swig_types, 60, 0, 0, 0, 0};
+#define SWIGTYPE_p_std__optionalT_double_t swig_types[42]
+#define SWIGTYPE_p_std__pairT_double_double_t swig_types[43]
+#define SWIGTYPE_p_std__vectorT_IAxis_const_p_std__allocatorT_IAxis_const_p_t_t swig_types[44]
+#define SWIGTYPE_p_std__vectorT_IAxis_p_std__allocatorT_IAxis_p_t_t swig_types[45]
+#define SWIGTYPE_p_std__vectorT_double_std__allocatorT_double_t_t swig_types[46]
+#define SWIGTYPE_p_std__vectorT_int_std__allocatorT_int_t_t swig_types[47]
+#define SWIGTYPE_p_std__vectorT_std__complexT_double_t_std__allocatorT_std__complexT_double_t_t_t swig_types[48]
+#define SWIGTYPE_p_std__vectorT_std__pairT_double_double_t_std__allocatorT_std__pairT_double_double_t_t_t swig_types[49]
+#define SWIGTYPE_p_std__vectorT_std__string_std__allocatorT_std__string_t_t swig_types[50]
+#define SWIGTYPE_p_std__vectorT_std__vectorT_double_std__allocatorT_double_t_t_std__allocatorT_std__vectorT_double_std__allocatorT_double_t_t_t_t swig_types[51]
+#define SWIGTYPE_p_std__vectorT_std__vectorT_int_std__allocatorT_int_t_t_std__allocatorT_std__vectorT_int_std__allocatorT_int_t_t_t_t swig_types[52]
+#define SWIGTYPE_p_std__vectorT_unsigned_int_std__allocatorT_unsigned_int_t_t swig_types[53]
+#define SWIGTYPE_p_std__vectorT_unsigned_long_std__allocatorT_unsigned_long_t_t swig_types[54]
+#define SWIGTYPE_p_swig__SwigPyIterator swig_types[55]
+#define SWIGTYPE_p_unsigned_char swig_types[56]
+#define SWIGTYPE_p_unsigned_int swig_types[57]
+#define SWIGTYPE_p_unsigned_long_long swig_types[58]
+#define SWIGTYPE_p_unsigned_short swig_types[59]
+#define SWIGTYPE_p_value_type swig_types[60]
+static swig_type_info *swig_types[62];
+static swig_module_info swig_module = {swig_types, 61, 0, 0, 0, 0};
 #define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)
 #define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)
 
@@ -25073,6 +25074,36 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_RotMatrix_angleAroundCoordAxis(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  RotMatrix *arg1 = (RotMatrix *) 0 ;
+  int arg2 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  int val2 ;
+  int ecode2 = 0 ;
+  PyObject *swig_obj[2] ;
+  std::optional< double > result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "RotMatrix_angleAroundCoordAxis", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_RotMatrix, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "RotMatrix_angleAroundCoordAxis" "', argument " "1"" of type '" "RotMatrix const *""'"); 
+  }
+  arg1 = reinterpret_cast< RotMatrix * >(argp1);
+  ecode2 = SWIG_AsVal_int(swig_obj[1], &val2);
+  if (!SWIG_IsOK(ecode2)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "RotMatrix_angleAroundCoordAxis" "', argument " "2"" of type '" "int""'");
+  } 
+  arg2 = static_cast< int >(val2);
+  result = ((RotMatrix const *)arg1)->angleAroundCoordAxis(arg2);
+  resultobj = SWIG_NewPointerObj((new std::optional< double >(static_cast< const std::optional< double >& >(result))), SWIGTYPE_p_std__optionalT_double_t, SWIG_POINTER_OWN |  0 );
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *RotMatrix_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
   PyObject *obj;
   if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL;
@@ -30177,6 +30208,11 @@ static PyMethodDef SwigMethods[] = {
 		"bool RotMatrix::isZRotation() const\n"
 		"\n"
 		""},
+	 { "RotMatrix_angleAroundCoordAxis", _wrap_RotMatrix_angleAroundCoordAxis, METH_VARARGS, "\n"
+		"RotMatrix_angleAroundCoordAxis(RotMatrix self, int iAxis) -> std::optional< double >\n"
+		"std::optional< double > RotMatrix::angleAroundCoordAxis(int iAxis) const\n"
+		"\n"
+		""},
 	 { "RotMatrix_swigregister", RotMatrix_swigregister, METH_O, NULL},
 	 { "RotMatrix_swiginit", RotMatrix_swiginit, METH_VARARGS, NULL},
 	 { "new_Bin1D", _wrap_new_Bin1D, METH_VARARGS, "\n"
@@ -30945,6 +30981,7 @@ static swig_type_info _swigt__p_std__complexT_double_t = {"_p_std__complexT_doub
 static swig_type_info _swigt__p_std__invalid_argument = {"_p_std__invalid_argument", "std::invalid_argument *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_std__lessT_std__string_t = {"_p_std__lessT_std__string_t", "std::less< std::string > *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t = {"_p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t", "std::map< std::string,double,std::less< std::string >,std::allocator< std::pair< std::string const,double > > > *|std::map< std::string,double > *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_std__optionalT_double_t = {"_p_std__optionalT_double_t", "std::optional< double > *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_std__pairT_double_double_t = {"_p_std__pairT_double_double_t", "std::pair< double,double > *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_std__vectorT_IAxis_const_p_std__allocatorT_IAxis_const_p_t_t = {"_p_std__vectorT_IAxis_const_p_std__allocatorT_IAxis_const_p_t_t", "std::vector< IAxis const *,std::allocator< IAxis const * > > *", 0, 0, (void*)0, 0};
 static swig_type_info _swigt__p_std__vectorT_IAxis_p_std__allocatorT_IAxis_p_t_t = {"_p_std__vectorT_IAxis_p_std__allocatorT_IAxis_p_t_t", "std::vector< IAxis *,std::allocator< IAxis * > > *", 0, 0, (void*)0, 0};
@@ -31007,6 +31044,7 @@ static swig_type_info *swig_type_initial[] = {
   &_swigt__p_std__invalid_argument,
   &_swigt__p_std__lessT_std__string_t,
   &_swigt__p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t,
+  &_swigt__p_std__optionalT_double_t,
   &_swigt__p_std__pairT_double_double_t,
   &_swigt__p_std__vectorT_IAxis_const_p_std__allocatorT_IAxis_const_p_t_t,
   &_swigt__p_std__vectorT_IAxis_p_std__allocatorT_IAxis_p_t_t,
@@ -31069,6 +31107,7 @@ static swig_cast_info _swigc__p_std__complexT_double_t[] = {  {&_swigt__p_std__c
 static swig_cast_info _swigc__p_std__invalid_argument[] = {  {&_swigt__p_std__invalid_argument, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_std__lessT_std__string_t[] = {  {&_swigt__p_std__lessT_std__string_t, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t[] = {  {&_swigt__p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_std__optionalT_double_t[] = {  {&_swigt__p_std__optionalT_double_t, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_std__pairT_double_double_t[] = {  {&_swigt__p_std__pairT_double_double_t, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_std__vectorT_IAxis_const_p_std__allocatorT_IAxis_const_p_t_t[] = {  {&_swigt__p_std__vectorT_IAxis_const_p_std__allocatorT_IAxis_const_p_t_t, 0, 0, 0},{0, 0, 0, 0}};
 static swig_cast_info _swigc__p_std__vectorT_IAxis_p_std__allocatorT_IAxis_p_t_t[] = {  {&_swigt__p_std__vectorT_IAxis_p_std__allocatorT_IAxis_p_t_t, 0, 0, 0},{0, 0, 0, 0}};
@@ -31131,6 +31170,7 @@ static swig_cast_info *swig_cast_initial[] = {
   _swigc__p_std__invalid_argument,
   _swigc__p_std__lessT_std__string_t,
   _swigc__p_std__mapT_std__string_double_std__lessT_std__string_t_std__allocatorT_std__pairT_std__string_const_double_t_t_t,
+  _swigc__p_std__optionalT_double_t,
   _swigc__p_std__pairT_double_double_t,
   _swigc__p_std__vectorT_IAxis_const_p_std__allocatorT_IAxis_const_p_t_t,
   _swigc__p_std__vectorT_IAxis_p_std__allocatorT_IAxis_p_t_t,
-- 
GitLab