From 8a214bb69634f51cc3db7ed64c78609d6051ad8f Mon Sep 17 00:00:00 2001
From: Paul Gessinger <paul.gessinger@cern.ch>
Date: Mon, 12 Feb 2018 13:05:08 +0100
Subject: [PATCH] add facility to produce PlaneSurfaces from variant_data

---
 .../include/ACTS/Surfaces/RectangleBounds.hpp |   2 +
 Core/include/ACTS/Surfaces/TriangleBounds.hpp |   2 +
 .../ACTS/Utilities/InstanceFactory.hpp        |  52 ++++++
 Core/include/ACTS/Utilities/VariantData.hpp   | 156 ++++++++++++++++--
 Core/src/Surfaces/PlaneSurface.cpp            |  36 +++-
 Core/src/Surfaces/RectangleBounds.cpp         |  14 ++
 Core/src/Surfaces/TriangleBounds.cpp          |  24 +++
 Tests/Surfaces/PlaneSurfaceTests.cpp          |  20 ++-
 8 files changed, 283 insertions(+), 23 deletions(-)
 create mode 100644 Core/include/ACTS/Utilities/InstanceFactory.hpp

diff --git a/Core/include/ACTS/Surfaces/RectangleBounds.hpp b/Core/include/ACTS/Surfaces/RectangleBounds.hpp
index ddbb41470..43af5380a 100644
--- a/Core/include/ACTS/Surfaces/RectangleBounds.hpp
+++ b/Core/include/ACTS/Surfaces/RectangleBounds.hpp
@@ -44,6 +44,8 @@ public:
   /// @param halfY halflength in Y
   RectangleBounds(double halfX, double halfY);
 
+  RectangleBounds(const variant_data& data);
+
   virtual ~RectangleBounds();
 
   virtual RectangleBounds*
diff --git a/Core/include/ACTS/Surfaces/TriangleBounds.hpp b/Core/include/ACTS/Surfaces/TriangleBounds.hpp
index d86f29913..5fc123ae8 100644
--- a/Core/include/ACTS/Surfaces/TriangleBounds.hpp
+++ b/Core/include/ACTS/Surfaces/TriangleBounds.hpp
@@ -51,6 +51,8 @@ public:
   /// @param vertices is the vector of vertices
   TriangleBounds(const std::array<Vector2D, 3>& vertices);
 
+  TriangleBounds(const variant_data &data);
+
   virtual ~TriangleBounds();
 
   virtual TriangleBounds*
diff --git a/Core/include/ACTS/Utilities/InstanceFactory.hpp b/Core/include/ACTS/Utilities/InstanceFactory.hpp
new file mode 100644
index 000000000..26ea38f76
--- /dev/null
+++ b/Core/include/ACTS/Utilities/InstanceFactory.hpp
@@ -0,0 +1,52 @@
+// This file is part of the ACTS project.
+//
+// Copyright (C) 2018 ACTS project team
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef ACTS_PLUGINS_JSONPLUGIN_INSTANCEFACTORY_H
+#define ACTS_PLUGINS_JSONPLUGIN_INSTANCEFACTORY_H 1
+
+#include <boost/functional/value_factory.hpp>
+#include <map>
+#include "ACTS/Utilities/VariantData.hpp"
+#include "ACTS/Utilities/ThrowAssert.hpp"
+
+#include "ACTS/Surfaces/RectangleBounds.hpp"
+#include "ACTS/Surfaces/TriangleBounds.hpp"
+
+namespace Acts {
+
+using PlanarBoundsPtr     = std::shared_ptr<const PlanarBounds>;
+using PlanarBoundsFactory = std::function<PlanarBoundsPtr(const variant_data&)>;
+
+class InstanceFactory
+{
+public:
+  InstanceFactory()
+  {
+    // set up map to store factories
+    m_planarBounds["RectangleBounds"] = [](auto const& data) {
+      return std::make_shared<const RectangleBounds>(data);
+    };
+    m_planarBounds["TriangleBounds"] = [](auto const& data) {
+      return std::make_shared<const TriangleBounds>(data);
+    };
+  }
+
+  PlanarBoundsPtr
+  planarBounds(const std::string& cname, const variant_data& data)
+  {
+    throw_assert(m_planarBounds.count(cname), "No factory found for class "+cname);
+    return m_planarBounds.at(cname)(data);
+  }
+
+private:
+  std::map<std::string, PlanarBoundsFactory> m_planarBounds;
+};
+
+}  // namespace Acts
+
+#endif
diff --git a/Core/include/ACTS/Utilities/VariantData.hpp b/Core/include/ACTS/Utilities/VariantData.hpp
index 3d8adec48..9b9f86a65 100644
--- a/Core/include/ACTS/Utilities/VariantData.hpp
+++ b/Core/include/ACTS/Utilities/VariantData.hpp
@@ -17,6 +17,7 @@
 #include <vector>
 
 #include "ACTS/Utilities/Definitions.hpp"
+#include "ACTS/Utilities/ThrowAssert.hpp"
 
 namespace Acts {
 
@@ -35,11 +36,11 @@ class variant_map;
 class variant_vector;
 
 using variant_data = boost::variant<int,
-                                     double,
-                                     std::string,
-                                     bool,
-                                     boost::recursive_wrapper<variant_map>,
-                                     boost::recursive_wrapper<variant_vector>>;
+                                    double,
+                                    std::string,
+                                    bool,
+                                    boost::recursive_wrapper<variant_map>,
+                                    boost::recursive_wrapper<variant_vector>>;
 
 class variant_map
 {
@@ -63,6 +64,20 @@ public:
     return m_map.count(key);
   }
 
+  template <class T>
+  T&
+  get(const std::string& key)
+  {
+    return boost::get<T>(m_map.at(key));
+  }
+
+  template <class T>
+  const T&
+  get(const std::string& key) const
+  {
+    return boost::get<T>(m_map.at(key));
+  }
+
   iterator
   begin()
   {
@@ -110,10 +125,31 @@ public:
     return m_vector.at(idx);
   }
 
+  const variant_data&
+  at(size_t idx) const
+  {
+    return m_vector.at(idx);
+  }
+
   void
-  push_back(variant_data&& data) {
+  push_back(variant_data&& data)
+  {
     m_vector.push_back(data);
   }
+  
+  template <class T>
+  T&
+  get(const size_t& idx)
+  {
+    return boost::get<T>(m_vector.at(idx));
+  }
+
+  template <class T>
+  const T&
+  get(const size_t& idx) const
+  {
+    return boost::get<T>(m_vector.at(idx));
+  }
 
   iterator
   begin()
@@ -140,7 +176,6 @@ private:
   vector_t m_vector;
 };
 
-
 class variant_json_visitor : public boost::static_visitor<>
 {
 public:
@@ -248,8 +283,7 @@ private:
   }
 };
 
-inline
-std::string
+inline std::string
 to_json(const variant_data& data, bool pretty = false)
 {
   variant_json_visitor jv(pretty);
@@ -257,24 +291,116 @@ to_json(const variant_data& data, bool pretty = false)
   return jv.str();
 }
 
-inline
-std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const variant_data& data)
 {
   os << to_json(data, true) << std::endl;
   return os;
 }
 
-inline
-variant_map
-to_variant(const Vector2D &vec) {
+inline variant_map
+to_variant(const Vector2D& vec)
+{
   using namespace std::string_literals;
   variant_map data;
-  data["type"] = "Vector2D"s;
+  data["type"]    = "Vector2D"s;
   data["payload"] = variant_vector({vec[0], vec[1]});
   return data;
 }
 
+inline variant_map
+to_variant(const Transform3D& trf)
+{
+  using namespace std::string_literals;
+  variant_map data;
+  data["type"] = "Transform3D"s;
+
+  variant_map payload;
+  // payload["matrix"] = to_variant(trf.matrix());
+  variant_vector matrix_data;
+  for (size_t i = 0; i < 4; i++) {
+    for (size_t j = 0; j < 4; j++) {
+      matrix_data.push_back(trf(i, j));
+    }
+  }
+  payload["data"] = matrix_data;
+
+  data["payload"] = payload;
+  return data;
+}
+
+inline variant_map
+to_variant(const ActsMatrixD<4, 4>& matrix)
+{
+  using namespace std::string_literals;
+  variant_map data;
+  data["type"] = "Matrix4x4"s;
+
+  variant_map payload;
+  payload["cols"] = 4;
+  payload["rows"] = 4;
+
+  variant_vector matrix_data;
+  for (size_t i = 0; i < 4; i++) {
+    for (size_t j = 0; j < 4; j++) {
+      matrix_data.push_back(matrix(i, j));
+    }
+  }
+  payload["data"] = matrix_data;
+
+  data["payload"] = payload;
+
+  return data;
+}
+
+template<typename T>
+inline T
+from_variant(const variant_data& data_);
+
+template<>
+inline Transform3D
+from_variant<Transform3D>(const variant_data& data_)
+{
+  throw_assert(data_.which() == 4, "Must be variant_map");
+  const variant_map& data = boost::get<variant_map>(data_);
+  throw_assert(data.get<std::string>("type") == "Transform3D",
+               "Must be type Transform3D");
+
+  const variant_map &payload = data.get<variant_map>("payload");
+
+  const variant_vector &matrix_data = payload.get<variant_vector>("data");
+  Transform3D trf;
+  for(size_t i=0;i<4;i++) {
+    for(size_t j=0;j<4;j++) {
+
+      size_t k = i*4+j;
+      double value = matrix_data.get<double>(k);
+      trf(i, j) = value;
+    }
+  }
+
+  return trf;
+}
+
+template<>
+inline Vector2D
+from_variant<Vector2D>(const variant_data& data_)
+{
+  throw_assert(data_.which() == 4, "Must be variant_map");
+  const variant_map& data = boost::get<variant_map>(data_);
+  throw_assert(data.get<std::string>("type") == "Vector2D",
+               "Must be type Vector2D");
+
+  const variant_vector &vector_data = data.get<variant_vector>("payload");
+
+  Vector2D vec;
+  for(size_t i=0;i<2;i++) {
+    vec[i] = vector_data.get<double>(i);
+  }
+
+  return vec;
+}
+
 /*int
 main()
 {
diff --git a/Core/src/Surfaces/PlaneSurface.cpp b/Core/src/Surfaces/PlaneSurface.cpp
index e0910140f..1484895e3 100644
--- a/Core/src/Surfaces/PlaneSurface.cpp
+++ b/Core/src/Surfaces/PlaneSurface.cpp
@@ -19,8 +19,10 @@
 #include "ACTS/Surfaces/InfiniteBounds.hpp"
 #include "ACTS/Surfaces/RectangleBounds.hpp"
 #include "ACTS/Utilities/Identifier.hpp"
-#include "ACTS/Utilities/VariantData.hpp"
 #include "ACTS/Utilities/ThrowAssert.hpp"
+#include "ACTS/Utilities/VariantData.hpp"
+
+#include "ACTS/Utilities/InstanceFactory.hpp"
 
 Acts::PlaneSurface::PlaneSurface(const PlaneSurface& other)
   : GeometryObject(), Surface(other), m_bounds(other.m_bounds)
@@ -74,12 +76,30 @@ Acts::PlaneSurface::PlaneSurface(std::shared_ptr<const Transform3D>  htrans,
 Acts::PlaneSurface::PlaneSurface(const variant_data& data_)
 {
   // we need to figure out which way the PS was constructed before
-  throw_assert(data_.which() == 5, "Variant data must be map");
+  throw_assert(data_.which() == 4, "Variant data must be map");
   variant_map data = boost::get<variant_map>(data_);
-  throw_assert(data.count("type") && data["type"] == "PlaneSurface",
-               "Variant data must have type and type must be PlaneSurface");
-
-  std::cout << "go unpack" << std::endl;
+  throw_assert(data.count("type"), "Variant data must have type.");
+  // std::string type = boost::get<std::string>(data["type"]);
+  std::string type = data.get<std::string>("type");
+  throw_assert(type == "PlaneSurface",
+               "Variant data type must be PlaneSurface");
+
+  variant_map payload    = data.get<variant_map>("payload");
+  variant_map bounds     = payload.get<variant_map>("bounds");
+  std::string boundsType = bounds.get<std::string>("type");
+
+  InstanceFactory                     factory;
+  std::shared_ptr<const PlanarBounds> pbounds
+      = factory.planarBounds(boundsType, bounds);
+
+  m_bounds = pbounds;
+
+  if (payload.count("transform")) {
+    // we have a transform
+    auto trf = std::make_shared<const Transform3D>(
+        from_variant<Transform3D>(payload.get<variant_map>("transform")));
+    m_transform = trf;
+  }
 }
 
 Acts::PlaneSurface::~PlaneSurface() {}
@@ -187,6 +207,10 @@ Acts::PlaneSurface::toVariantData() const
   variant_data bounds = m_bounds->toVariantData();
   payload["bounds"]   = bounds;
 
+  if (m_transform) {
+    payload["transform"] = to_variant(*m_transform);
+  }
+
   variant_map data;
   data["type"]    = "PlaneSurface"s;
   data["payload"] = payload;
diff --git a/Core/src/Surfaces/RectangleBounds.cpp b/Core/src/Surfaces/RectangleBounds.cpp
index eb25fdd13..b2df2556a 100644
--- a/Core/src/Surfaces/RectangleBounds.cpp
+++ b/Core/src/Surfaces/RectangleBounds.cpp
@@ -12,6 +12,7 @@
 
 #include "ACTS/Surfaces/RectangleBounds.hpp"
 #include "ACTS/Utilities/VariantData.hpp"
+#include "ACTS/Utilities/ThrowAssert.hpp"
 
 #include <cmath>
 #include <iomanip>
@@ -22,6 +23,19 @@ Acts::RectangleBounds::RectangleBounds(double halex, double haley)
 {
 }
 
+Acts::RectangleBounds::RectangleBounds(const variant_data& data_)
+{
+  throw_assert(data_.which() == 4, "Variant data must be map");
+  const variant_map &data = boost::get<variant_map>(data_);
+  std::string type = data.get<std::string>("type");
+  throw_assert(type == "RectangleBounds", "Type must be RectangleBounds");
+
+  const variant_map &payload = data.get<variant_map>("payload");
+
+  m_halfX = payload.get<double>("halflengthX");
+  m_halfY = payload.get<double>("halflengthY");
+}
+
 Acts::RectangleBounds::~RectangleBounds()
 {
 }
diff --git a/Core/src/Surfaces/TriangleBounds.cpp b/Core/src/Surfaces/TriangleBounds.cpp
index c705def38..48e9fe4d9 100644
--- a/Core/src/Surfaces/TriangleBounds.cpp
+++ b/Core/src/Surfaces/TriangleBounds.cpp
@@ -28,6 +28,30 @@ Acts::TriangleBounds::TriangleBounds(const std::array<Vector2D, 3>& vertices)
   m_boundingBox = RectangleBounds(mx, my);
 }
 
+Acts::TriangleBounds::TriangleBounds(const variant_data& data_)
+  : m_vertices(), m_boundingBox(0, 0)
+{
+  throw_assert(data_.which() == 4, "Variant data must be map");
+  const variant_map &data = boost::get<variant_map>(data_);
+  std::string type = data.get<std::string>("type");
+  throw_assert(type == "TriangleBounds", "Type must be TriangleBounds");
+
+  const variant_map &payload = data.get<variant_map>("payload");
+  const variant_vector &vertices = payload.get<variant_vector>("vertices");
+  throw_assert(vertices.size() == 3, "Vertices for triangle must be exactly 3.");
+
+  double mx = 0, my = 0;
+  for(size_t i=0;i<3;i++) {
+    Vector2D vtx = from_variant<Vector2D>(vertices.at(i));
+    mx = std::max(mx, std::abs(vtx.x()));
+    my = std::max(my, std::abs(vtx.y()));
+    m_vertices.at(i) = vtx;
+  }
+
+  m_boundingBox = RectangleBounds(mx, my);
+}
+
+
 Acts::TriangleBounds::~TriangleBounds()
 {
 }
diff --git a/Tests/Surfaces/PlaneSurfaceTests.cpp b/Tests/Surfaces/PlaneSurfaceTests.cpp
index d1effa22e..b31ce94f7 100644
--- a/Tests/Surfaces/PlaneSurfaceTests.cpp
+++ b/Tests/Surfaces/PlaneSurfaceTests.cpp
@@ -201,20 +201,36 @@ namespace Test {
     // build
     auto rectBounds = std::make_shared<const RectangleBounds>(5, 10);
     auto idTrf = std::make_shared<const Transform3D>(Transform3D::Identity());
-    PlaneSurface rectSrf(idTrf, rectBounds);
+    auto rot = std::make_shared<const Transform3D>(AngleAxis3D(M_PI/4., Vector3D::UnitZ()));
+    PlaneSurface rectSrf(rot, rectBounds);
     variant_data rectVariant = rectSrf.toVariantData();
     std::cout << rectVariant << std::endl;
 
     // rebuild from variant
     PlaneSurface rectSrfRec(rectVariant);
+    auto rectBoundsRec = dynamic_cast<const RectangleBounds*>(&rectSrfRec.bounds());
+    BOOST_CHECK_CLOSE(rectBounds->halflengthX(), rectBoundsRec->halflengthX(), 1e-4);
+    BOOST_CHECK_CLOSE(rectBounds->halflengthY(), rectBoundsRec->halflengthY(), 1e-4);
+    BOOST_TEST(rot->isApprox(rectSrfRec.transform(), 1e-4));
 
 
     std::array<Vector2D, 3> vertices = {{Vector2D(1,1), Vector2D(1, -1), Vector2D(-1, 1)}};
     auto triangleBounds = std::make_shared<const TriangleBounds>(vertices);
-    PlaneSurface triangleSrf(idTrf, triangleBounds);
+    PlaneSurface triangleSrf(rot, triangleBounds);
     variant_data triangleVariant = triangleSrf.toVariantData();
     std::cout << triangleVariant << std::endl;
 
+    // rebuild
+    PlaneSurface triangleSrfRec(triangleVariant);
+    auto triangleBoundsRec = dynamic_cast<const TriangleBounds*>(&triangleSrfRec.bounds());
+    for(size_t i=0;i<3;i++) {
+      Vector2D exp = triangleBounds->vertices().at(i);
+      Vector2D act = triangleBoundsRec->vertices().at(i);
+      BOOST_CHECK_CLOSE(exp.x(), act.x(), 1e-4);
+      BOOST_CHECK_CLOSE(exp.y(), act.y(), 1e-4);
+    }
+    BOOST_TEST(rot->isApprox(triangleSrfRec.transform(), 1e-4));
+
   }
   
 
-- 
GitLab