diff --git a/GeoModelCore/GeoModelHelpers/GeoModelHelpers/TransformToStringConverter.h b/GeoModelCore/GeoModelHelpers/GeoModelHelpers/TransformToStringConverter.h
index 5e15a85a0a572d2913ce0f816baf5e5c8a5404b9..cb912668e5596ffb0b3db9e37c784805ff3bb428 100644
--- a/GeoModelCore/GeoModelHelpers/GeoModelHelpers/TransformToStringConverter.h
+++ b/GeoModelCore/GeoModelHelpers/GeoModelHelpers/TransformToStringConverter.h
@@ -28,6 +28,10 @@ namespace GeoTrf{
     std::string toString(const Transform3D& trans, bool useCoordAngles = false, int precision = 4);
     std::string toString(const EulerAngles& angles, int precision = 4);
     std::string toString(const CoordEulerAngles& angles, int precision = 4);
+
+    std::string toString(const Line3D& line, int precision = 4);
+    std::string toString(const Plane3D& plane, int precision = 4);
+
 }
 
 #endif
\ No newline at end of file
diff --git a/GeoModelCore/GeoModelHelpers/src/TransformToStringConverter.cxx b/GeoModelCore/GeoModelHelpers/src/TransformToStringConverter.cxx
index 13f5c885b0f6c14603af2a36abc9f360d774ee41..cef0fb0d9bec1dbebdc8fcc3fe4e9962747055f1 100644
--- a/GeoModelCore/GeoModelHelpers/src/TransformToStringConverter.cxx
+++ b/GeoModelCore/GeoModelHelpers/src/TransformToStringConverter.cxx
@@ -56,4 +56,15 @@ namespace GeoTrf{
         ostr <<"gamma: "<<angles.gamma * toDeg;        
         return ostr.str();
     }
+    std::string toString(const Line3D& line, int precision) {
+        std::stringstream ostr{};
+        ostr<<"line --- "<<toString(line.position(), precision)<<" + "<<toString(line.direction(), precision);
+        return ostr.str();
+    }
+    std::string toString(const Plane3D& plane, int precision) {
+        std::stringstream ostr{};
+        ostr << std::setiosflags(std::ios::fixed) << std::setprecision(precision);
+        ostr<<"plane -- normal: "<<toString(plane.normal(), 4)<<" - C: "<<plane.offSet();
+        return ostr.str();
+    }
 }
diff --git a/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoDefinitions.h b/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoDefinitions.h
index efe8538f8215b0d0feabacff3ed5d855742cfe8e..d4a85fa6fe12ae022e1204e89d89b84a0f38f801 100644
--- a/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoDefinitions.h
+++ b/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoDefinitions.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
 */
 
 #ifndef GEOMODELKERNEL_GEODEFINITIONS_H
@@ -14,64 +14,115 @@
 #else
 #include <Eigen/Dense>
 #endif
+#include <optional>
 
 namespace GeoTrf {
   using Rotation3D = Eigen::Quaternion<double>;
   using Translation3D = Eigen::Translation<double, 3>;
   using AngleAxis3D =  Eigen::AngleAxisd;
   using Transform3D = Eigen::Affine3d;
-  template<size_t N> using VectorN =  Eigen::Matrix<double, N, 1>;
+  template<int N> using VectorN =  Eigen::Matrix<double, N, 1>;
   using Vector3D = VectorN<3>;
   using Vector2D = VectorN<2>;
   using RotationMatrix3D =  Eigen::Matrix<double, 3, 3>;
   using Diagonal3DMatrix = Eigen::DiagonalMatrix <double, 3>;
 
-  /// check if we can use Eigen Hyperplane for this
-  struct Plane3D {
-  protected:
-    double a_{0.};
-    double b_{0.};
-    double c_{1.};
-    double d_{0.};
-    
-  public:
-    /**
-     *  Default constructor - creates plane z=0. */
-    Plane3D() = default;
-    /**
-     * Constructor from four numbers - creates plane a*x+b*y+c*z+d=0. */
-    Plane3D(double a1, double b1, double c1, double d1)
-    :  a_(a1), b_(b1), c_(c1), d_(d1) 
-    {}
+  /** @brief Returns a unit vector from an arbitrary vector */
+  template <int N>
+    VectorN<N> unit(const VectorN<N>& a);
+  /** @brief Helper struct describing a Line in 3D space */
+  class Plane3D;
+  class Line3D{
+    private:
+      Vector3D m_pos{Vector3D::Zero()};
+      Vector3D m_dir{Vector3D::Zero()};
+    public:
+       /** @brief Default empty constructor */
+       Line3D() = default;
+       /*** @brief Constructor taking a point on the line and the line direction
+        *   @param pos: Point on the line
+        *   @param dir: Direction vector (Needs to be normalized) */
+       Line3D(const Vector3D& pos, const Vector3D& dir):
+          m_pos{pos},
+          m_dir{dir} {}
+        /** @brief Returns the position of the line */
+        const Vector3D& position() const {
+            return m_pos;
+        }
+        /** @brief Returns the direction of the line */
+        const Vector3D& direction() const {
+            return m_dir;
+        }
+        /** @brief Travels a distance x along the line */
+        GeoTrf::Vector3D travel(const double x) const {
+          return position() + x * direction();
+        }
+        /** @brief Returns the distance of the point along the line */
+        double distance(const Vector3D& v) const {
+            return direction().dot(v-position());
+        }
+        /** @brief Returns the intersection point with another line
+         *  @param other: Other line to intersect */
+        std::optional<Vector3D> intersect(const Line3D& other) const;
+        /** @brief the intersection point with a plane */
+        std::optional<Vector3D> intersect(const Plane3D& other) const;
+  };
+  /** @brief Helper struct describing a Plane in 3D dimensional space */
+  class Plane3D {
+    private:
+      Vector3D m_normal{Vector3D::Zero()};
+      double m_offSet{0.};    
+    public:
+      /**
+       *  Default constructor - creates plane z=0. */
+      Plane3D() = default;
+      /**
+       * Constructor from four numbers - creates plane a*x+b*y+c*z+d=0. */
+      Plane3D(double a1, double b1, double c1, double d1): 
+        m_normal{a1,b1,c1},
+        m_offSet{d1} {}
 
-    /**
-     * Constructor from normal and point. */
-    Plane3D(const Vector3D& n, const Vector3D& p)
-    : a_(n.x()), b_(n.y()), c_(n.z()), d_(-n.dot(p)) 
-    {}
+      /*** Constructor from normal and point. */
+      Plane3D(const Vector3D& n, const Vector3D& p): 
+        m_normal{n},
+        m_offSet{-n.dot(p)} {}
+      
+      /** @brief Constructor from two direction vectors and one point in the plane
+       *  @param dir1: First direction along the plane
+       *  @param dir2: Second direction vector along the plane
+       *  @param point: Arbitrary point inside the plane */
+      Plane3D(const Vector3D& dir1,
+              const Vector3D& dir2,
+              const Vector3D& point):
+        Plane3D{unit(dir1.cross(dir2)), point} {}
     
     /**
      * Returns the a-coefficient in the plane equation: a*x+b*y+c*z+d=0. */
-    double a() const { return a_; }
+    double a() const { return m_normal.x(); }
     /**
      * Returns the b-coefficient in the plane equation: a*x+b*y+c*z+d=0. */
-    double b() const { return b_; }
+    double b() const { return m_normal.y(); }
     /**
      * Returns the c-coefficient in the plane equation: a*x+b*y+c*z+d=0. */
-    double c() const { return c_; }
+    double c() const { return m_normal.z(); }
     /**
      * Returns the free member of the plane equation: a*x+b*y+c*z+d=0. */
-    double d() const { return d_; }
+    double d() const { return m_offSet; }
     
-    /**
-     * Returns normal. */
-    Vector3D normal() const { return Vector3D(a_,b_,c_); }
+    /** @brief Returns the plane normal vector */
+    const Vector3D& normal() const { return m_normal; }
+    /** @brief Returns the offset  or the d of the plane equation*/
+    double offSet() const { return m_offSet; }
+    /** @brief Returns the intersection of the plane with a line */
+    std::optional<Vector3D> intersect(const Line3D& other) const;
+    /** @brief Returns whether a point is inside a plane */
+    bool inside(const Vector3D& point) const;
   };
 
   class Translate3D : public Transform3D {
   public:
     
-    Translate3D(const GeoTrf::Vector3D& v):
+    Translate3D(const Vector3D& v):
       Translate3D{v.x(),v.y(),v.z()}{}
 
     Translate3D(double x, double y, double z)
@@ -264,5 +315,5 @@ namespace GeoTrf {
     virtual ~GeoTransformRT() = default;
   };
 }
-
+#include "GeoModelKernel/GeoDefinitions.icc"
 #endif // GEOMODELKERNEL_GEODEFINITIONS_H
diff --git a/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoDefinitions.icc b/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoDefinitions.icc
new file mode 100644
index 0000000000000000000000000000000000000000..90c2d6f9b68c15af57a654e99392159440ba51b3
--- /dev/null
+++ b/GeoModelCore/GeoModelKernel/GeoModelKernel/GeoDefinitions.icc
@@ -0,0 +1,38 @@
+/*
+  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
+*/
+#ifndef GEOMODELKERNEL_GEODEFINITIONS_ICC
+#define GEOMODELKERNEL_GEODEFINITIONS_ICC
+
+#include <climits>
+namespace GeoTrf{
+    template <int N>
+        VectorN<N> unit(const VectorN<N>& a) {
+        return a / std::sqrt(a.dot(a));
+    }   
+    inline std::optional<Vector3D> Line3D::intersect(const Line3D& other) const {
+        const double dirDots = direction().dot(other.direction());
+        const double divisor = 1. - dirDots*dirDots;
+        if (std::abs(divisor) < std::numeric_limits<double>::epsilon()){
+                return std::nullopt;
+        }
+        const Vector3D AminusB = other.position() - position();
+        const double lambda = (AminusB.dot(direction()) - AminusB.dot(other.direction()) * dirDots) / divisor;
+        return std::make_optional<Vector3D>(position()+ lambda*direction());
+    }
+    inline std::optional<Vector3D> Line3D::intersect(const Plane3D& other) const {
+        const double normDot = other.normal().dot(direction());
+        if (std::abs(normDot) < std::numeric_limits<double>::epsilon()) {
+            return std::nullopt;
+        }
+        const double lambda = (other.offSet() - position().dot(other.normal())) / normDot;
+        return std::make_optional<Vector3D>(position() + lambda * direction());
+    }
+    inline std::optional<Vector3D> Plane3D::intersect(const Line3D& other) const {
+        return other.intersect(*this);
+    }
+    inline bool Plane3D::inside(const Vector3D& point) const {
+        return (normal().dot(point) - offSet()) < std::numeric_limits<double>::epsilon();
+    }
+}
+#endif
\ No newline at end of file
diff --git a/GeoModelCore/GeoModelKernel/tests/testPlaneAndLine.cxx b/GeoModelCore/GeoModelKernel/tests/testPlaneAndLine.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..46fd96360aaeec77557f58184cba6a9b1221c18d
--- /dev/null
+++ b/GeoModelCore/GeoModelKernel/tests/testPlaneAndLine.cxx
@@ -0,0 +1,82 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+ */
+#include "GeoModelKernel/GeoDefinitions.h"
+#include "GeoModelKernel/Units.h"
+
+#include <stdlib.h>
+#include <iostream>
+
+using namespace GeoTrf;
+
+constexpr double tolerance = 1.e-9;
+std::ostream& operator<<( std::ostream& ostr, const Vector3D& v) {
+    ostr<<"("<<v.x()<<", "<<v.y()<<", "<<v.z()<<")";
+    return ostr;
+}
+std::ostream & operator<<(std::ostream& ostr, const Line3D& l) {
+    ostr<<"line "<<l.position()<<" + "<<l.direction();
+    return ostr;
+}
+std::ostream& operator<<(std::ostream& ostr, const Plane3D& p) {
+    ostr<<"Plane with normal: "<<p.normal()<<" & offset: "<<p.offSet();
+    return ostr;
+}
+
+#define ERROR(msg)                                                  \
+    {                                                               \
+        std::cerr<<__FILE__<<":"<<__LINE__<<" - "<<msg<<std::endl;  \
+        return EXIT_FAILURE;                                        \
+    }                                                               \
+
+int main() {
+    
+    
+    const Line3D lineA{ Vector3D{10.,0.,0.}, Vector3D::UnitX()};
+    const Line3D lineB{ Vector3D{30.,0.,0.}, Vector3D::UnitY()};
+
+    const std::optional<Vector3D> isectAtoB = lineA.intersect(lineB);
+    
+    if (!isectAtoB) ERROR("The two lines A "<<lineA<<" & B "<<lineB<<" are not intersecting");
+
+    const std::optional<Vector3D> isectBtoA = lineB.intersect(lineA);
+    if (!isectAtoB) ERROR("The two lines B "<<lineB<<" & A "<<lineA<<" are not intersecting");
+    /// Check that the two lines intersect in the same point
+    const GeoTrf::Vector3D iSectDiff = (*isectAtoB) - (*isectBtoA);
+    if (iSectDiff.dot(iSectDiff) > tolerance) 
+        ERROR("Intersections ended up at two different points "<<(*isectAtoB)<<" vs. "<<(*isectBtoA));
+    const GeoTrf::Vector3D iSectToExpect = (*isectAtoB) - Vector3D{30.,0.,0.};
+    if (iSectToExpect.dot(iSectToExpect) > tolerance) 
+        ERROR("Intersections ended up at two different points "<<(*isectAtoB)<<" vs. "<<lineB.position());
+
+    /// Now make it a bit more difficult
+    for (const double dist : {-10., 10.,  45. ,100., 666., 1789.}) {
+        for (double angle =0; angle<=180.*GeoModelKernelUnits::deg; angle+=15.*GeoModelKernelUnits::deg){
+            const GeoTrf::Vector3D dir{0.,std::cos(angle), std::sin(angle)};
+            const Line3D test{lineA.travel(dist) - dist*dir, dir};
+
+            auto isectWithTest = lineA.intersect(test);
+            if (!isectWithTest) ERROR("Intersection of "<<lineA<<" with "<<test<<" failed.");
+            const Vector3D backTest = (*isectWithTest) - lineA.travel(lineA.distance(*isectWithTest)); 
+            if (backTest.dot(backTest) > tolerance)
+                ERROR("Intersection with "<<lineA<<" recuperated a different travel distance: "
+                     <<lineA.distance(*isectWithTest)<<" vs. "<<dist);   
+            if (std::abs(dist - test.distance(*isectWithTest))> tolerance)
+                ERROR("Intersection with "<<test<<" recuperated a different travel distance: "
+                     <<test.distance(*isectWithTest)<<" vs. "<<dist);   
+        }
+    }
+    Plane3D plane{lineA.direction(), lineB.direction(), lineA.position()};
+
+    if (!plane.inside(lineA.position()))
+        ERROR("The point "<<lineA.position()<<" defining the plane "<<plane<<" is not inside");
+    
+    Line3D testLine{300.*plane.normal(), plane.normal()};
+    auto planeIsect = testLine.intersect(plane);
+    if(!planeIsect || !plane.inside(*planeIsect))
+        ERROR("The intersection point "<<planeIsect.value_or(Vector3D::Zero())<<" is outside plane"
+             <<plane);
+
+    return EXIT_SUCCESS;
+}
+