From 2d2e1a91b420a34fb075ef4add21bcea020d94f7 Mon Sep 17 00:00:00 2001
From: Nikos Karastathis <n.karastathis@kit.edu>
Date: Mon, 25 Jan 2021 13:51:24 +0100
Subject: [PATCH] added and tested Path class in geometry

---
 corsika/detail/framework/geometry/Path.inl |  59 ++++++++++
 corsika/framework/geometry/Path.hpp        | 124 +++++++++++++++++++++
 tests/framework/testGeometry.cpp           |  63 ++++++++++-
 3 files changed, 245 insertions(+), 1 deletion(-)
 create mode 100644 corsika/detail/framework/geometry/Path.inl
 create mode 100644 corsika/framework/geometry/Path.hpp

diff --git a/corsika/detail/framework/geometry/Path.inl b/corsika/detail/framework/geometry/Path.inl
new file mode 100644
index 000000000..c8c05921f
--- /dev/null
+++ b/corsika/detail/framework/geometry/Path.inl
@@ -0,0 +1,59 @@
+///*
+// * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+// *
+// * This software is distributed under the terms of the GNU General Public
+// * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+// * the license.
+// */
+//
+//#pragma once
+//
+//#include <deque>
+//#include <corsika/framework/geometry/Point.hpp>
+//
+//namespace corsika {
+//
+//  inline void AddToEnd(Point const& point) {
+//    length_ += (point - points_.back()).getNorm();
+//    points_.push_back(point);
+//  }
+//
+//
+//  inline void RemoveFromEnd() {
+//    auto lastpoint_ = points_.back();
+//    points_.pop_back();
+//    int dequesize_ = points_.size();
+//    if (dequesize_ == 0 || dequesize_ == 1) {
+//      length_ = LengthType::zero();
+//    }
+//    else if (dequesize_ == 2) {
+//      length_ = (points_.back() - points_.front()).getNorm();
+//    }
+//    else { length_ -= (lastpoint_ - points_.back()).getNorm(); }
+//  }
+//
+//
+//  inline LengthType GetLength() const {
+//    return length_;
+//  }
+//
+//
+//  inline Point GetStart() const {
+//    return points_.front();
+//  }
+//
+//
+//  inline Point GetEnd() const {
+//    return points_.back();
+//  }
+//
+//
+//  inline Point GetPoint(std::size_t const index) const {
+//    return points_.at(index);
+//  }
+//
+//
+//
+//  inline int GetNSegments() const { return points_.size() - 1; }
+//
+//} // namespace corsika
\ No newline at end of file
diff --git a/corsika/framework/geometry/Path.hpp b/corsika/framework/geometry/Path.hpp
new file mode 100644
index 000000000..77b6966e5
--- /dev/null
+++ b/corsika/framework/geometry/Path.hpp
@@ -0,0 +1,124 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#pragma once
+
+#include <deque>
+#include <corsika/framework/geometry/Point.hpp>
+
+namespace corsika {
+
+  /**
+   * This class represents a (potentially) curved path between two
+   * points using N >= 1 straight-line segments.
+   */
+  class Path {
+    std::deque<Point> points_;     ///< The points that make up this path.
+    LengthType length_= LengthType::zero(); ///< The length of the path.
+  public:
+    /**
+     * Create a Path with a given starting Point.
+     */
+    Path(Point const& point) {
+      points_.push_front(point);
+    }
+
+    /**
+     * Initialize a Path from an existing collection of Points.
+     */
+    Path(std::deque<Point> const& points)
+        : points_(points) {
+      int dequesize_ = points.size();
+      if (dequesize_ == 0 || dequesize_ == 1) {
+        length_ = LengthType::zero();
+      }
+      else if (dequesize_ == 2) {
+        length_ = (points.back() - points.front()).getNorm();
+      }
+      else {
+        for (auto point = points.begin(); point !=  points.end() - 1; ++point) {
+          auto point_next = *(point+1);
+          auto point_now = *(point);
+          length_ += (point_next - point_now).getNorm();
+        }
+      }
+    }
+
+    /**
+     * Add a new Point to the end of the path.
+     */
+    void AddToEnd(Point const& point) {
+      length_ += (point - points_.back()).getNorm();
+      points_.push_back(point);
+    }
+
+    /**
+     * Remove a point from the end of the path.
+     */
+    void RemoveFromEnd() {
+      auto lastpoint_ = points_.back();
+      points_.pop_back();
+      int dequesize_ = points_.size();
+      if (dequesize_ == 0 || dequesize_ == 1) {
+        length_ = LengthType::zero();
+      }
+      else if (dequesize_ == 2) {
+        length_ = (points_.back() - points_.front()).getNorm();
+      }
+      else { length_ -= (lastpoint_ - points_.back()).getNorm(); }
+    }
+
+    /**
+     * Get the total length of the path.
+     */
+    LengthType GetLength() const {
+      return length_;
+    }
+
+    /**
+     * Get the starting point of the path.
+     */
+    Point GetStart() const {
+      return points_.front();
+    }
+
+    /**
+     * Get the end point of the path.
+     */
+    Point GetEnd() const {
+      return points_.back();
+    }
+
+    /**
+     * Get a specific point of the path.
+     */
+    Point GetPoint(std::size_t const index) const {
+      return points_.at(index);
+    }
+
+    /**
+     * Return an iterator to the start of the Path.
+     */
+    auto begin() { return points_.begin(); }
+
+    /**
+     * Return an iterator to the end of the Path.
+     */
+    auto end() { return points_.end(); }
+
+    /**
+     * Get the number of steps in the path.
+     *
+     * This is one less than the number of points that
+     * defines the path.
+     */
+    int GetNSegments() const { return points_.size() - 1; }
+
+  };  // class Path
+
+} // namespace corsika
\ No newline at end of file
diff --git a/tests/framework/testGeometry.cpp b/tests/framework/testGeometry.cpp
index 8153b3a4e..8ae12854d 100644
--- a/tests/framework/testGeometry.cpp
+++ b/tests/framework/testGeometry.cpp
@@ -14,12 +14,13 @@
 #include <corsika/framework/geometry/Line.hpp>
 #include <corsika/framework/geometry/Helix.hpp>
 #include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Path.hpp>
 #include <corsika/framework/geometry/RootCoordinateSystem.hpp>
 #include <corsika/framework/geometry/Sphere.hpp>
 #include <corsika/framework/geometry/StraightTrajectory.hpp>
 #include <corsika/framework/geometry/LeapFrogTrajectory.hpp>
 
-#include <PhysicalUnitsCatch2.hpp> // namespace corsike::testing
+#include <PhysicalUnitsCatch2.hpp> // namespace corsika::testing
 
 using namespace corsika;
 using namespace corsika::testing;
@@ -332,3 +333,63 @@ TEST_CASE("Point") {
   CHECK(p3.distance_to(p4) / 1_m == Approx(4));
   CHECK(p5.distance_to(p6) / 1_m == Approx(1));
 }
+
+
+
+TEST_CASE("Path") {
+  //define a known CS
+  CoordinateSystemPtr root = get_root_CoordinateSystem();
+
+  //define known points
+  Point p1(root, {0_m, 0_m, 0_m});
+  Point p2(root, {0_m, 0_m, 1_m});
+  Point p3(root, {0_m, 0_m, 2_m});
+  Point p4(root, {0_m, 0_m, 3_m});
+  Point p5(root, {0_m, 0_m, 4_m});
+  //define paths
+  Path P1(p1);
+  Path P2({p1,p2});
+  Path P3({p1, p2, p3});
+  //define deque that include point(s)
+  std::deque<Point> l1 = {p1};
+  std::deque<Point> l2 = {p1, p2};
+  std::deque<Point> l3 = {p1, p2, p3};
+
+  //test the various path constructors
+  SECTION("Test Constructors") {
+    //check constructor for one point
+    CHECK(std::equal(P1.begin(), P1.end(), l1.begin(),[](Point a, Point b)
+    { return (a - b).getNorm() / 1_m < 1e-5;}));
+    //check constructor for collection of points
+    CHECK(std::equal(P3.begin(), P3.end(), l3.begin(),[](Point a, Point b)
+    { return (a - b).getNorm() / 1_m < 1e-5;}));
+  }
+
+    //test the length and access methods
+  SECTION("Test GetLength() and modifications to Path") {
+    P1.AddToEnd(p2);
+    P2.RemoveFromEnd();
+    //Check modifications to path
+    CHECK(std::equal(P1.begin(), P1.end(), l2.begin(),[](Point a, Point b)
+    { return (a - b).getNorm() / 1_m < 1e-5;}));
+    CHECK(std::equal(P2.begin(), P2.end(), l1.begin(),[](Point a, Point b)
+    { return (a - b).getNorm() / 1_m < 1e-5;}));
+    //Check GetStart(), GetEnd(), GetPoint()
+    CHECK((P3.GetEnd() - P3.GetStart()).getNorm() / 1_m == Approx(2));
+    CHECK((P1.GetPoint(1) - p2).getNorm() / 1_m == Approx(0));
+    //Check GetLength()
+    CHECK(P1.GetLength() / 1_m == Approx(1));
+    CHECK(P2.GetLength() / 1_m == Approx(0));
+    CHECK(P3.GetLength() / 1_m == Approx(2));
+    P2.RemoveFromEnd();
+    CHECK(P2.GetLength() / 1_m == Approx(0)); //Check the length of an empty path
+    P3.AddToEnd(p4);
+    P3.AddToEnd(p5);
+    CHECK(P3.GetLength() / 1_m == Approx(4));
+    P3.RemoveFromEnd();
+    CHECK(P3.GetLength() / 1_m == Approx(3)); //Check RemoveFromEnd() else case
+    //Check GetNSegments()
+    CHECK(P3.GetNSegments() - 3 == Approx(0));
+
+  }
+}
\ No newline at end of file
-- 
GitLab