From fa5866c0d8a85c64edaffe5027db4594234d7231 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Mon, 7 Aug 2023 17:35:45 +0200
Subject: [PATCH] scaffold for plotting rescaled axes

---
 Base/Axis/Frame.cpp                           |  8 ++
 Base/Axis/Frame.h                             |  2 +
 Base/Axis/Scale.cpp                           |  5 ++
 Base/Axis/Scale.h                             |  2 +
 Device/Data/Datafield.cpp                     |  5 ++
 Device/Data/Datafield.h                       |  1 +
 .../scatter2d/AxesInDifferentUnits.py         |  9 ++-
 .../scatter2d/AxesInDifferentUnits.py         |  9 ++-
 auto/Wrap/libBornAgainBase.py                 |  8 ++
 auto/Wrap/libBornAgainBase_wrap.cpp           | 76 +++++++++++++++++++
 auto/Wrap/libBornAgainDevice.py               |  4 +
 auto/Wrap/libBornAgainDevice_wrap.cpp         | 38 ++++++++++
 rawEx/scatter2d/AxesInDifferentUnits.py       |  9 ++-
 13 files changed, 164 insertions(+), 12 deletions(-)

diff --git a/Base/Axis/Frame.cpp b/Base/Axis/Frame.cpp
index 2142b974bab..b93466af383 100644
--- a/Base/Axis/Frame.cpp
+++ b/Base/Axis/Frame.cpp
@@ -147,6 +147,14 @@ Frame* Frame::plottableFrame() const
     return new Frame(std::move(outaxes));
 }
 
+Frame* Frame::rescaledFrame(const std::string& unit) const
+{
+    std::vector<const Scale*> outaxes;
+    for (const Scale* s : m_axes)
+        outaxes.emplace_back(new Scale(s->rescaledScale(unit)));
+    return new Frame(std::move(outaxes));
+}
+
 Frame* Frame::flat() const
 {
     std::vector<const Scale*> outaxes;
diff --git a/Base/Axis/Frame.h b/Base/Axis/Frame.h
index 1cfba5a41e5..b2137cb9e38 100644
--- a/Base/Axis/Frame.h
+++ b/Base/Axis/Frame.h
@@ -17,6 +17,7 @@
 
 #include "Base/Types/ICloneable.h"
 #include "Base/Types/OwningVector.h"
+#include <string>
 
 using std::size_t;
 
@@ -82,6 +83,7 @@ public:
 #endif // SWIG
 
     Frame* plottableFrame() const;
+    Frame* rescaledFrame(const std::string& unit) const;
     Frame* flat() const;
 
 protected:
diff --git a/Base/Axis/Scale.cpp b/Base/Axis/Scale.cpp
index 9e11a885491..889d81579ff 100644
--- a/Base/Axis/Scale.cpp
+++ b/Base/Axis/Scale.cpp
@@ -204,3 +204,8 @@ Scale Scale::plottableScale() const
     }
     return *this;
 }
+
+Scale Scale::rescaledScale(const std::string& /*unit*/) const
+{
+    return *this;
+}
diff --git a/Base/Axis/Scale.h b/Base/Axis/Scale.h
index a9c540991c0..296a3dab4ac 100644
--- a/Base/Axis/Scale.h
+++ b/Base/Axis/Scale.h
@@ -17,6 +17,7 @@
 
 #include "Base/Axis/Bin.h"
 #include "Base/Axis/Coordinate.h"
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -77,6 +78,7 @@ public:
     std::string unit() const;
 
     Scale plottableScale() const;
+    Scale rescaledScale(const std::string& unit) const;
 
 protected:
     Coordinate m_coord;
diff --git a/Device/Data/Datafield.cpp b/Device/Data/Datafield.cpp
index 7e1673566f2..240d66f7e89 100644
--- a/Device/Data/Datafield.cpp
+++ b/Device/Data/Datafield.cpp
@@ -334,6 +334,11 @@ Datafield Datafield::plottableField() const
     return {title(), frame().plottableFrame(), m_values, m_errSigmas};
 }
 
+Datafield Datafield::rescaled(const std::string& unit) const
+{
+    return {title(), frame().rescaledFrame(unit), m_values, m_errSigmas};
+}
+
 Datafield Datafield::flat() const
 {
     return {title(), frame().flat(), m_values, m_errSigmas};
diff --git a/Device/Data/Datafield.h b/Device/Data/Datafield.h
index b0f0dbc921c..02230408e26 100644
--- a/Device/Data/Datafield.h
+++ b/Device/Data/Datafield.h
@@ -82,6 +82,7 @@ public:
     //... modifiers
 
     Datafield plottableField() const;
+    Datafield rescaled(const std::string& unit) const;
     Datafield flat() const;
 
     //! Multiplies contents by constant factor, e.g. to shift curves in log plot
diff --git a/auto/Examples/scatter2d/AxesInDifferentUnits.py b/auto/Examples/scatter2d/AxesInDifferentUnits.py
index b33adec9229..68e50bfcc7e 100755
--- a/auto/Examples/scatter2d/AxesInDifferentUnits.py
+++ b/auto/Examples/scatter2d/AxesInDifferentUnits.py
@@ -49,10 +49,11 @@ if __name__ == '__main__':
     rcParams['image.aspect'] = 'auto'
     plt.figure(figsize=(10, 10))
 
-    transformed_plot(1, result, "Default: real-space detector coordinates")
-    transformed_plot(2, result, "Bin indices")
-    transformed_plot(3, result, "Deflection angles")
-    transformed_plot(4, result, "Q space")
+    transformed_plot(1, result.rescaled("mm"),
+                     "Default: real-space detector coordinates")
+    transformed_plot(2, result.rescaled("#bin"), "Bin indices")
+    transformed_plot(3, result.rescaled("deg"), "Deflection angles")
+    transformed_plot(4, result.rescaled("1/nm"), "Q space")
 
     # finish plot
     plt.subplots_adjust(
diff --git a/auto/MiniExamples/scatter2d/AxesInDifferentUnits.py b/auto/MiniExamples/scatter2d/AxesInDifferentUnits.py
index 40bcc347b2b..39bf056bc4e 100755
--- a/auto/MiniExamples/scatter2d/AxesInDifferentUnits.py
+++ b/auto/MiniExamples/scatter2d/AxesInDifferentUnits.py
@@ -49,10 +49,11 @@ if __name__ == '__main__':
     rcParams['image.aspect'] = 'auto'
     plt.figure(figsize=(10, 10))
 
-    transformed_plot(1, result, "Default: real-space detector coordinates")
-    transformed_plot(2, result, "Bin indices")
-    transformed_plot(3, result, "Deflection angles")
-    transformed_plot(4, result, "Q space")
+    transformed_plot(1, result.rescaled("mm"),
+                     "Default: real-space detector coordinates")
+    transformed_plot(2, result.rescaled("#bin"), "Bin indices")
+    transformed_plot(3, result.rescaled("deg"), "Deflection angles")
+    transformed_plot(4, result.rescaled("1/nm"), "Q space")
 
     # finish plot
     plt.subplots_adjust(
diff --git a/auto/Wrap/libBornAgainBase.py b/auto/Wrap/libBornAgainBase.py
index a5c67adf214..069f3f05bc6 100644
--- a/auto/Wrap/libBornAgainBase.py
+++ b/auto/Wrap/libBornAgainBase.py
@@ -1944,6 +1944,10 @@ class Scale(object):
     def plottableScale(self):
         r"""plottableScale(Scale self) -> Scale"""
         return _libBornAgainBase.Scale_plottableScale(self)
+
+    def rescaledScale(self, unit):
+        r"""rescaledScale(Scale self, std::string const & unit) -> Scale"""
+        return _libBornAgainBase.Scale_rescaledScale(self, unit)
     __swig_destroy__ = _libBornAgainBase.delete_Scale
 
 # Register Scale in _libBornAgainBase:
@@ -2031,6 +2035,10 @@ class Frame(ICloneable):
         r"""plottableFrame(Frame self) -> Frame"""
         return _libBornAgainBase.Frame_plottableFrame(self)
 
+    def rescaledFrame(self, unit):
+        r"""rescaledFrame(Frame self, std::string const & unit) -> Frame"""
+        return _libBornAgainBase.Frame_rescaledFrame(self, unit)
+
     def flat(self):
         r"""flat(Frame self) -> Frame"""
         return _libBornAgainBase.Frame_flat(self)
diff --git a/auto/Wrap/libBornAgainBase_wrap.cpp b/auto/Wrap/libBornAgainBase_wrap.cpp
index 43651e8983c..e512a1806a7 100644
--- a/auto/Wrap/libBornAgainBase_wrap.cpp
+++ b/auto/Wrap/libBornAgainBase_wrap.cpp
@@ -25988,6 +25988,43 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Scale_rescaledScale(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Scale *arg1 = (Scale *) 0 ;
+  std::string *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  int res2 = SWIG_OLDOBJ ;
+  PyObject *swig_obj[2] ;
+  SwigValueWrapper< Scale > result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "Scale_rescaledScale", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Scale, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Scale_rescaledScale" "', argument " "1"" of type '" "Scale const *""'"); 
+  }
+  arg1 = reinterpret_cast< Scale * >(argp1);
+  {
+    std::string *ptr = (std::string *)0;
+    res2 = SWIG_AsPtr_std_string(swig_obj[1], &ptr);
+    if (!SWIG_IsOK(res2)) {
+      SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Scale_rescaledScale" "', argument " "2"" of type '" "std::string const &""'"); 
+    }
+    if (!ptr) {
+      SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "Scale_rescaledScale" "', argument " "2"" of type '" "std::string const &""'"); 
+    }
+    arg2 = ptr;
+  }
+  result = ((Scale const *)arg1)->rescaledScale((std::string const &)*arg2);
+  resultobj = SWIG_NewPointerObj((new Scale(result)), SWIGTYPE_p_Scale, SWIG_POINTER_OWN |  0 );
+  if (SWIG_IsNewObj(res2)) delete arg2;
+  return resultobj;
+fail:
+  if (SWIG_IsNewObj(res2)) delete arg2;
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_delete_Scale(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   Scale *arg1 = (Scale *) 0 ;
@@ -26756,6 +26793,43 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Frame_rescaledFrame(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Frame *arg1 = (Frame *) 0 ;
+  std::string *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  int res2 = SWIG_OLDOBJ ;
+  PyObject *swig_obj[2] ;
+  Frame *result = 0 ;
+  
+  if (!SWIG_Python_UnpackTuple(args, "Frame_rescaledFrame", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Frame, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Frame_rescaledFrame" "', argument " "1"" of type '" "Frame const *""'"); 
+  }
+  arg1 = reinterpret_cast< Frame * >(argp1);
+  {
+    std::string *ptr = (std::string *)0;
+    res2 = SWIG_AsPtr_std_string(swig_obj[1], &ptr);
+    if (!SWIG_IsOK(res2)) {
+      SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Frame_rescaledFrame" "', argument " "2"" of type '" "std::string const &""'"); 
+    }
+    if (!ptr) {
+      SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "Frame_rescaledFrame" "', argument " "2"" of type '" "std::string const &""'"); 
+    }
+    arg2 = ptr;
+  }
+  result = (Frame *)((Frame const *)arg1)->rescaledFrame((std::string const &)*arg2);
+  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Frame, 0 |  0 );
+  if (SWIG_IsNewObj(res2)) delete arg2;
+  return resultobj;
+fail:
+  if (SWIG_IsNewObj(res2)) delete arg2;
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_Frame_flat(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   Frame *arg1 = (Frame *) 0 ;
@@ -28822,6 +28896,7 @@ static PyMethodDef SwigMethods[] = {
 	 { "Scale___eq__", _wrap_Scale___eq__, METH_VARARGS, "Scale___eq__(Scale self, Scale right) -> bool"},
 	 { "Scale_unit", _wrap_Scale_unit, METH_O, "Scale_unit(Scale self) -> std::string"},
 	 { "Scale_plottableScale", _wrap_Scale_plottableScale, METH_O, "Scale_plottableScale(Scale self) -> Scale"},
+	 { "Scale_rescaledScale", _wrap_Scale_rescaledScale, METH_VARARGS, "Scale_rescaledScale(Scale self, std::string const & unit) -> Scale"},
 	 { "delete_Scale", _wrap_delete_Scale, METH_O, "delete_Scale(Scale self)"},
 	 { "Scale_swigregister", Scale_swigregister, METH_O, NULL},
 	 { "Scale_swiginit", Scale_swiginit, METH_VARARGS, NULL},
@@ -28848,6 +28923,7 @@ static PyMethodDef SwigMethods[] = {
 	 { "Frame_hasSameSizes", _wrap_Frame_hasSameSizes, METH_VARARGS, "Frame_hasSameSizes(Frame self, Frame arg2) -> bool"},
 	 { "Frame___eq__", _wrap_Frame___eq__, METH_VARARGS, "Frame___eq__(Frame self, Frame arg2) -> bool"},
 	 { "Frame_plottableFrame", _wrap_Frame_plottableFrame, METH_O, "Frame_plottableFrame(Frame self) -> Frame"},
+	 { "Frame_rescaledFrame", _wrap_Frame_rescaledFrame, METH_VARARGS, "Frame_rescaledFrame(Frame self, std::string const & unit) -> Frame"},
 	 { "Frame_flat", _wrap_Frame_flat, METH_O, "Frame_flat(Frame self) -> Frame"},
 	 { "Frame_swigregister", Frame_swigregister, METH_O, NULL},
 	 { "Frame_swiginit", Frame_swiginit, METH_VARARGS, NULL},
diff --git a/auto/Wrap/libBornAgainDevice.py b/auto/Wrap/libBornAgainDevice.py
index 4f2afd7ce34..0b96014c25a 100644
--- a/auto/Wrap/libBornAgainDevice.py
+++ b/auto/Wrap/libBornAgainDevice.py
@@ -2141,6 +2141,10 @@ class Datafield(object):
         r"""plottableField(Datafield self) -> Datafield"""
         return _libBornAgainDevice.Datafield_plottableField(self)
 
+    def rescaled(self, unit):
+        r"""rescaled(Datafield self, std::string const & unit) -> Datafield"""
+        return _libBornAgainDevice.Datafield_rescaled(self, unit)
+
     def flat(self):
         r"""flat(Datafield self) -> Datafield"""
         return _libBornAgainDevice.Datafield_flat(self)
diff --git a/auto/Wrap/libBornAgainDevice_wrap.cpp b/auto/Wrap/libBornAgainDevice_wrap.cpp
index 8357e4bc40f..77151d93e0a 100644
--- a/auto/Wrap/libBornAgainDevice_wrap.cpp
+++ b/auto/Wrap/libBornAgainDevice_wrap.cpp
@@ -28860,6 +28860,43 @@ fail:
 }
 
 
+SWIGINTERN PyObject *_wrap_Datafield_rescaled(PyObject *self, PyObject *args) {
+  PyObject *resultobj = 0;
+  Datafield *arg1 = (Datafield *) 0 ;
+  std::string *arg2 = 0 ;
+  void *argp1 = 0 ;
+  int res1 = 0 ;
+  int res2 = SWIG_OLDOBJ ;
+  PyObject *swig_obj[2] ;
+  SwigValueWrapper< Datafield > result;
+  
+  if (!SWIG_Python_UnpackTuple(args, "Datafield_rescaled", 2, 2, swig_obj)) SWIG_fail;
+  res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_Datafield, 0 |  0 );
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "Datafield_rescaled" "', argument " "1"" of type '" "Datafield const *""'"); 
+  }
+  arg1 = reinterpret_cast< Datafield * >(argp1);
+  {
+    std::string *ptr = (std::string *)0;
+    res2 = SWIG_AsPtr_std_string(swig_obj[1], &ptr);
+    if (!SWIG_IsOK(res2)) {
+      SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "Datafield_rescaled" "', argument " "2"" of type '" "std::string const &""'"); 
+    }
+    if (!ptr) {
+      SWIG_exception_fail(SWIG_ValueError, "invalid null reference " "in method '" "Datafield_rescaled" "', argument " "2"" of type '" "std::string const &""'"); 
+    }
+    arg2 = ptr;
+  }
+  result = ((Datafield const *)arg1)->rescaled((std::string const &)*arg2);
+  resultobj = SWIG_NewPointerObj((new Datafield(result)), SWIGTYPE_p_Datafield, SWIG_POINTER_OWN |  0 );
+  if (SWIG_IsNewObj(res2)) delete arg2;
+  return resultobj;
+fail:
+  if (SWIG_IsNewObj(res2)) delete arg2;
+  return NULL;
+}
+
+
 SWIGINTERN PyObject *_wrap_Datafield_flat(PyObject *self, PyObject *args) {
   PyObject *resultobj = 0;
   Datafield *arg1 = (Datafield *) 0 ;
@@ -37827,6 +37864,7 @@ static PyMethodDef SwigMethods[] = {
 	 { "Datafield_maxVal", _wrap_Datafield_maxVal, METH_O, "Datafield_maxVal(Datafield self) -> double"},
 	 { "Datafield_minVal", _wrap_Datafield_minVal, METH_O, "Datafield_minVal(Datafield self) -> double"},
 	 { "Datafield_plottableField", _wrap_Datafield_plottableField, METH_O, "Datafield_plottableField(Datafield self) -> Datafield"},
+	 { "Datafield_rescaled", _wrap_Datafield_rescaled, METH_VARARGS, "Datafield_rescaled(Datafield self, std::string const & unit) -> Datafield"},
 	 { "Datafield_flat", _wrap_Datafield_flat, METH_O, "Datafield_flat(Datafield self) -> Datafield"},
 	 { "Datafield_scale", _wrap_Datafield_scale, METH_VARARGS, "Datafield_scale(Datafield self, double factor)"},
 	 { "Datafield_crop", _wrap_Datafield_crop, METH_VARARGS, "\n"
diff --git a/rawEx/scatter2d/AxesInDifferentUnits.py b/rawEx/scatter2d/AxesInDifferentUnits.py
index 9edfc6fb202..cc99d890418 100755
--- a/rawEx/scatter2d/AxesInDifferentUnits.py
+++ b/rawEx/scatter2d/AxesInDifferentUnits.py
@@ -49,10 +49,11 @@ if __name__ == '__main__':
     rcParams['image.aspect'] = 'auto'
     plt.figure(figsize=(10, 10))
 
-    transformed_plot(1, result, "Default: real-space detector coordinates")
-    transformed_plot(2, result, "Bin indices")
-    transformed_plot(3, result, "Deflection angles")
-    transformed_plot(4, result, "Q space")
+    transformed_plot(1, result.rescaled("mm"),
+                     "Default: real-space detector coordinates")
+    transformed_plot(2, result.rescaled("#bin"), "Bin indices")
+    transformed_plot(3, result.rescaled("deg"), "Deflection angles")
+    transformed_plot(4, result.rescaled("1/nm"), "Q space")
 
     # finish plot
     plt.subplots_adjust(
-- 
GitLab