Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-17 08:35:35

0001 #ifndef VECGEOM_SURFACE_FRAMEMASKS_H
0002 #define VECGEOM_SURFACE_FRAMEMASKS_H
0003 
0004 #ifndef SURF_ACCURATE_SAFETY
0005 #define SURF_ACCURATE_SAFETY 0
0006 #endif
0007 
0008 #define WINDOW_ACCURATE_SAFETY SURF_ACCURATE_SAFETY
0009 #define QUAD_ACCURATE_SAFETY SURF_ACCURATE_SAFETY
0010 
0011 #include <VecGeom/surfaces/mask/WindowMask.h>
0012 #include <VecGeom/surfaces/mask/RingMask.h>
0013 #include <VecGeom/surfaces/mask/ZPhiMask.h>
0014 #include <VecGeom/surfaces/mask/TriangleMask.h>
0015 #include <VecGeom/surfaces/mask/QuadrilateralMask.h>
0016 
0017 namespace vgbrep {
0018 
0019 namespace detail {
0020 template <typename Real_t>
0021 static void ConvertAngleToFrame1(Vector3D<Real_t> &sPhi, Vector3D<Real_t> &ePhi, TransformationMP<Real_t> const &trans)
0022 {
0023   bool flip       = trans.Rotation(8) < 0.;
0024   auto trans_sPhi = trans.InverseTransformDirection(sPhi);
0025   auto trans_ePhi = trans.InverseTransformDirection(ePhi);
0026   sPhi            = flip ? trans_ePhi : trans_sPhi;
0027   ePhi            = flip ? trans_sPhi : trans_ePhi;
0028 }
0029 } // namespace detail
0030 
0031 /// @brief Frame checker utility
0032 /// @tparam Real_t Precision type
0033 /// @tparam FrameType1 Parent frame type
0034 /// @tparam FrameType2 hild frame type
0035 template <typename Real_t, typename FrameType1, typename FrameType2>
0036 struct FrameChecker {
0037 
0038   /// @brief Check if a child frame is embedded in a parent frame
0039   /// @param f1 Parent frame
0040   /// @param f2 Child frame
0041   /// @param trans Transformation of the child in the parent reference system
0042   /// @return Child frame contained in the parent one
0043   static bool IsEmbedding(FrameType1 const &f1, FrameType2 const &f2, TransformationMP<Real_t> const &trans)
0044   {
0045     VECGEOM_LOG(error) << "Frame embedding not implemented for " << typeid(FrameType1).name() << " - "
0046                        << typeid(FrameType2).name();
0047     return false;
0048   }
0049 
0050   /// @brief Check if two frames of arbitrary types are disjoint
0051   /// @param f1 First frame
0052   /// @param f2 Second frame
0053   /// @param trans Transformation of the second frame in the reference frame of the first
0054   /// @return Frames are disjoint
0055   static bool AreDisjoint(FrameType1 const &f1, FrameType2 const &f2, TransformationMP<Real_t> const &trans)
0056   {
0057     VECGEOM_LOG(error) << "Frame disjoint check not implemented for " << typeid(FrameType1).name() << " - "
0058                        << typeid(FrameType2).name();
0059     return false;
0060   }
0061 
0062   /// @brief Full check of a frame against the other
0063   /// @param f1 First frame
0064   /// @param f2 Second frame
0065   /// @param trans Transformation of the second frame in the reference frame of the first
0066   /// @return Intersection type
0067   static FrameIntersect CheckFrames(FrameType1 const &f1, FrameType2 const &f2, TransformationMP<Real_t> const &trans)
0068   {
0069     if (FrameChecker<Real_t, FrameType1, FrameType2>::AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0070     auto intersect_type = FrameIntersect::kIntersect;
0071     if (FrameChecker<Real_t, FrameType1, FrameType2>::IsEmbedding(f1, f2, trans))
0072       intersect_type = FrameIntersect::kEmbedding;
0073     if (FrameChecker<Real_t, FrameType2, FrameType1>::IsEmbedding(f2, f1, trans.Inverse())) {
0074       if (intersect_type == FrameIntersect::kEmbedding)
0075         intersect_type = FrameIntersect::kEqual;
0076       else
0077         intersect_type = FrameIntersect::kEmbedded;
0078     }
0079     return intersect_type;
0080   }
0081 };
0082 
0083 template <typename Real_t>
0084 struct FrameChecker<Real_t, RingMask<Real_t>, RingMask<Real_t>> {
0085   static bool IsEmbedding(RingMask<Real_t> const &frame1, RingMask<Real_t> const &frame2,
0086                           TransformationMP<Real_t> const &trans)
0087   {
0088     // Special case where trans is identity (most frequent)
0089     if (!trans.HasTranslation()) {
0090       // Radial embedding
0091       if (frame2.rangeR[0] < vecgeom::MakeMinusTolerant<true, Real_t>(frame1.rangeR[0]) ||
0092           frame2.rangeR[1] > vecgeom::MakePlusTolerant<true, Real_t>(frame1.rangeR[1]))
0093         return false;
0094       // Phi embedding
0095       if (frame1.isFullCirc) return true;
0096       if (frame2.isFullCirc) return false;
0097       Vector3D<Real_t> sPhi{frame2.vecSPhi[0], frame2.vecSPhi[1], 0};
0098       Vector3D<Real_t> ePhi{frame2.vecEPhi[0], frame2.vecEPhi[1], 0};
0099       detail::ConvertAngleToFrame1(sPhi, ePhi, trans);
0100       AngleInterval<Real_t> ang1(frame1.vecSPhi.Phi(), frame1.vecEPhi.Phi());
0101       AngleInterval<Real_t> ang2(sPhi.Phi(), ePhi.Phi());
0102       bool embedding = ang1.Intersect(ang2) == ang2;
0103       return embedding;
0104     }
0105     // General case: check if Safety inside for the center of the frame2 circle is large enough
0106     Vector3D<Real_t> center;
0107     Real_t safety = frame1.SafetyInside(trans.InverseTransform(center));
0108     return frame2.rangeR[1] < vecgeom::MakePlusTolerant<true, Real_t>(safety);
0109   }
0110 
0111   static bool AreDisjoint(RingMask<Real_t> const &frame1, RingMask<Real_t> const &frame2,
0112                           TransformationMP<Real_t> const &trans)
0113   {
0114     // Concentric rings
0115     if (!trans.HasTranslation()) {
0116       // non-overlapping on radius
0117       if (frame2.rangeR[0] > vecgeom::MakeMinusTolerant<true, Real_t>(frame1.rangeR[1]) ||
0118           frame1.rangeR[0] > vecgeom::MakeMinusTolerant<true, Real_t>(frame2.rangeR[1]))
0119         return true;
0120       // one of the two is a full circle, while there is an overlap on R
0121       if (frame1.isFullCirc || frame2.isFullCirc) return false;
0122       // Phi check
0123       Vector3D<Real_t> sPhi{frame2.vecSPhi[0], frame2.vecSPhi[1], 0};
0124       Vector3D<Real_t> ePhi{frame2.vecEPhi[0], frame2.vecEPhi[1], 0};
0125       detail::ConvertAngleToFrame1(sPhi, ePhi, trans);
0126       AngleInterval<Real_t> ang1(frame1.vecSPhi.Phi(), frame1.vecEPhi.Phi());
0127       AngleInterval<Real_t> ang2(sPhi.Phi(), ePhi.Phi());
0128       bool disjoint = ang1.Intersect(ang2).IsNull();
0129       return disjoint;
0130     }
0131     // Two rings are disjoint if:
0132     // 1. The distance between their centers (norm of the translation) is larger than the sum of outer radii
0133     auto dist = trans.Translation().Length();
0134     if (frame1.rangeR[1] + frame2.rangeR[1] < dist + vecgeom::kToleranceDist<Real_t> * dist) return true;
0135     // 2. One of the inner holes of one ring embeds the other ring
0136     if (frame1.rangeR[0] - frame2.rangeR[1] > dist - vecgeom::kToleranceDist<Real_t> * dist ||
0137         frame2.rangeR[0] - frame1.rangeR[1] > dist - vecgeom::kToleranceDist<Real_t> * dist)
0138       return true;
0139     // 3. The bounding boxes are disjoint (relevant for rings having acute angles) **NOT CHECKED YET**
0140     return false;
0141   }
0142 
0143   static FrameIntersect CheckFrames(RingMask<Real_t> const &f1, RingMask<Real_t> const &f2,
0144                                     TransformationMP<Real_t> const &trans)
0145   {
0146     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0147     auto intersect_type = FrameIntersect::kIntersect;
0148     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0149     if (IsEmbedding(f2, f1, trans.Inverse())) {
0150       if (intersect_type == FrameIntersect::kEmbedding)
0151         intersect_type = FrameIntersect::kEqual;
0152       else
0153         intersect_type = FrameIntersect::kEmbedded;
0154     }
0155     return intersect_type;
0156   }
0157 };
0158 
0159 template <typename Real_t>
0160 struct FrameChecker<Real_t, RingMask<Real_t>, WindowMask<Real_t>> {
0161   static bool IsEmbedding(RingMask<Real_t> const &frame1, WindowMask<Real_t> const &frame2,
0162                           TransformationMP<Real_t> const &trans)
0163   {
0164     Vector3D<Real_t> vlocal[4] = {{frame2.rangeU[0], frame2.rangeV[0], 0},
0165                                   {frame2.rangeU[0], frame2.rangeV[1], 0},
0166                                   {frame2.rangeU[1], frame2.rangeV[1], 0},
0167                                   {frame2.rangeU[1], frame2.rangeV[0], 0}};
0168 
0169     Vector3D<Real_t> v[4];
0170     // If any vertex is not embedded, the frame is not embedded
0171     for (auto i = 0; i < 4; ++i) {
0172       v[i] = trans.InverseTransform(vlocal[i]);
0173       if (!frame1.Inside(v[i])) return false;
0174     }
0175     // Less than 180 deg ring having Rmin = 0 (convex)
0176     if (frame1.IsConvex()) return true;
0177     // General case: All segments of frame2 must not cross frame1 edges, and the center of frame1 must not be contained
0178     // in frame2
0179     Segment2D<Real_t> seg1_phi1({frame1.rangeR[0] * frame1.vecSPhi}, {frame1.rangeR[1] * frame1.vecSPhi});
0180     Segment2D<Real_t> seg1_phi2({frame1.rangeR[0] * frame1.vecEPhi}, {frame1.rangeR[1] * frame1.vecEPhi});
0181     for (size_t i = 0; i < 4; ++i) {
0182       Segment2D<Real_t> seg2({v[i].x(), v[i].y()}, {v[(i + 1) % 4].x(), v[(i + 1) % 4].y()});
0183       if (frame1.HasRmin()) {
0184         auto embedded = seg2.Intersect(Circle2D<Real_t>{frame1.rangeR[0], {Real_t(0), Real_t{0}}});
0185         if (embedded == SegmentIntersect::kEmbedded || embedded == SegmentIntersect::kIntersect) return false;
0186       }
0187       if (!frame1.isFullCirc) {
0188         auto embedded = seg1_phi1.Intersect(seg2);
0189         if (embedded != SegmentIntersect::kNoIntersect && embedded != SegmentIntersect::kEmbedding) return false;
0190         embedded = seg1_phi2.Intersect(seg2);
0191         if (embedded != SegmentIntersect::kNoIntersect && embedded != SegmentIntersect::kEmbedding) return false;
0192       }
0193     }
0194     if (frame1.isFullCirc && frame1.HasRmin()) {
0195       // The center of the circle must not be inside the frame
0196       auto center = trans.Transform(Vector3D<Real_t>{});
0197       if (frame2.Inside(center)) return false;
0198     }
0199 
0200     return true;
0201   }
0202 
0203   static bool AreDisjoint(RingMask<Real_t> const &frame1, WindowMask<Real_t> const &frame2,
0204                           TransformationMP<Real_t> const &trans)
0205   {
0206     // Fast check: The safety from outside of the center of the ring in the window frame is larger than the outer radius
0207     auto center = trans.Transform(Vector3D<Real_t>{});
0208     if (frame2.SafetyOutside(center) > frame1.rangeR[1] - vecgeom::kToleranceDist<Real_t> * frame1.rangeR[1])
0209       return true;
0210     // Fast check: The center of the box is inside the inner radius of the ring and its safety from outside is less than
0211     // the box diagonal
0212     center      = trans.InverseTransform(Vector3D<Real_t>{});
0213     auto diag2  = frame2.GetHalfSize().Length2();
0214     auto safety = frame1.SafetyOutside(center);
0215     if (center.Length2() < frame1.rangeR[0] * frame1.rangeR[0] &&
0216         diag2 < safety * safety + vecgeom::kToleranceDistSquared<Real_t>)
0217       return true;
0218     return false;
0219   }
0220 
0221   static FrameIntersect CheckFrames(RingMask<Real_t> const &f1, WindowMask<Real_t> const &f2,
0222                                     TransformationMP<Real_t> const &trans)
0223   {
0224     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0225     auto intersect_type = FrameIntersect::kIntersect;
0226     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0227     if (FrameChecker<Real_t, WindowMask<Real_t>, RingMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0228       if (intersect_type == FrameIntersect::kEmbedding)
0229         intersect_type = FrameIntersect::kEqual;
0230       else
0231         intersect_type = FrameIntersect::kEmbedded;
0232     }
0233     return intersect_type;
0234   }
0235 };
0236 
0237 template <typename Real_t>
0238 struct FrameChecker<Real_t, RingMask<Real_t>, TriangleMask<Real_t>> {
0239   static bool IsEmbedding(RingMask<Real_t> const &frame1, TriangleMask<Real_t> const &frame2,
0240                           TransformationMP<Real_t> const &trans)
0241   {
0242     Vector3D<Real_t> vlocal[3] = {{frame2.p_[0].x(), frame2.p_[0].y(), 0},
0243                                   {frame2.p_[1].x(), frame2.p_[1].y(), 0},
0244                                   {frame2.p_[2].x(), frame2.p_[2].y(), 0}};
0245 
0246     Vector3D<Real_t> v[3];
0247     // If any vertex is not embedded, the frame is not embedded
0248     for (auto i = 0; i < 3; ++i) {
0249       v[i] = trans.InverseTransform(vlocal[i]);
0250       if (!frame1.Inside(v[i])) return false;
0251     }
0252     // Less than 180 deg ring having Rmin = 0 (convex)
0253     if (frame1.IsConvex()) return true;
0254     // General case: All segments of frame2 must not cross frame1 edges, and the center of frame1 must not be contained
0255     // in frame2
0256     for (size_t i = 0; i < 3; ++i) {
0257       Segment2D<Real_t> seg2({v[i].x(), v[i].y()}, {v[(i + 1) % 3].x(), v[(i + 1) % 3].y()});
0258       if (frame1.HasRmin()) {
0259         auto embedded = seg2.Intersect(Circle2D<Real_t>{frame1.rangeR[0], {Real_t(0), Real_t{0}}});
0260         if (embedded == SegmentIntersect::kEmbedded || embedded == SegmentIntersect::kIntersect) return false;
0261       }
0262       if (!frame1.isFullCirc) {
0263         Segment2D<Real_t> seg1_phi1({frame1.rangeR[0] * frame1.vecSPhi}, {frame1.rangeR[1] * frame1.vecSPhi});
0264         Segment2D<Real_t> seg1_phi2({frame1.rangeR[0] * frame1.vecEPhi}, {frame1.rangeR[1] * frame1.vecEPhi});
0265         auto embedded = seg1_phi1.Intersect(seg2);
0266         if (embedded != SegmentIntersect::kNoIntersect && embedded != SegmentIntersect::kEmbedding) return false;
0267         embedded = seg1_phi2.Intersect(seg2);
0268         if (embedded != SegmentIntersect::kNoIntersect && embedded != SegmentIntersect::kEmbedding) return false;
0269       }
0270     }
0271     // The center of the circle must not be inside the frame
0272     if (!frame1.isFullCirc) {
0273       auto center = trans.Transform(Vector3D<Real_t>{});
0274       if (frame2.Inside(center)) return false;
0275     }
0276 
0277     return true;
0278   }
0279 
0280   static bool AreDisjoint(RingMask<Real_t> const &frame1, TriangleMask<Real_t> const &frame2,
0281                           TransformationMP<Real_t> const &trans)
0282   {
0283     // Fast check: The safety form outside of the center of the ring in the window frame is larger than the outer radius
0284     auto center = trans.Transform(Vector3D<Real_t>{});
0285     return (frame2.SafetyOutside(center) > frame1.rangeR[1] - vecgeom::kToleranceDist<Real_t> * frame1.rangeR[1]);
0286   }
0287 
0288   static FrameIntersect CheckFrames(RingMask<Real_t> const &f1, TriangleMask<Real_t> const &f2,
0289                                     TransformationMP<Real_t> const &trans)
0290   {
0291     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0292     auto intersect_type = FrameIntersect::kIntersect;
0293     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0294     if (FrameChecker<Real_t, TriangleMask<Real_t>, RingMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0295       if (intersect_type == FrameIntersect::kEmbedding)
0296         intersect_type = FrameIntersect::kEqual;
0297       else
0298         intersect_type = FrameIntersect::kEmbedded;
0299     }
0300     return intersect_type;
0301   }
0302 };
0303 
0304 template <typename Real_t>
0305 struct FrameChecker<Real_t, RingMask<Real_t>, QuadrilateralMask<Real_t>> {
0306   static bool IsEmbedding(RingMask<Real_t> const &frame1, QuadrilateralMask<Real_t> const &frame2,
0307                           TransformationMP<Real_t> const &trans)
0308   {
0309     Vector3D<Real_t> vlocal[4] = {{frame2.p_[0].x(), frame2.p_[0].y(), 0},
0310                                   {frame2.p_[1].x(), frame2.p_[1].y(), 0},
0311                                   {frame2.p_[2].x(), frame2.p_[2].y(), 0},
0312                                   {frame2.p_[3].x(), frame2.p_[3].y(), 0}};
0313 
0314     Vector3D<Real_t> v[4];
0315     // If any vertex is not embedded, the frame is not embedded
0316     for (auto i = 0; i < 4; ++i) {
0317       v[i] = trans.InverseTransform(vlocal[i]);
0318       if (!frame1.Inside(v[i])) return false;
0319     }
0320     // Less than 180 deg ring having Rmin = 0 (convex)
0321     if (frame1.IsConvex()) return true;
0322     // General case: All segments of frame2 must not cross frame1 edges, and the center of frame1 must not be contained
0323     // in frame2
0324     for (size_t i = 0; i < 4; ++i) {
0325       Segment2D<Real_t> seg2({v[i].x(), v[i].y()}, {v[(i + 1) % 4].x(), v[(i + 1) % 4].y()});
0326       if (frame1.HasRmin()) {
0327         auto embedded = seg2.Intersect(Circle2D<Real_t>{frame1.rangeR[0], {Real_t(0), Real_t{0}}});
0328         if (embedded == SegmentIntersect::kEmbedded || embedded == SegmentIntersect::kIntersect) return false;
0329       }
0330       if (!frame1.isFullCirc) {
0331         Segment2D<Real_t> seg1_phi1({frame1.rangeR[0] * frame1.vecSPhi}, {frame1.rangeR[1] * frame1.vecSPhi});
0332         Segment2D<Real_t> seg1_phi2({frame1.rangeR[0] * frame1.vecEPhi}, {frame1.rangeR[1] * frame1.vecEPhi});
0333         auto embedded = seg1_phi1.Intersect(seg2);
0334         if (embedded != SegmentIntersect::kNoIntersect && embedded != SegmentIntersect::kEmbedding) return false;
0335         embedded = seg1_phi2.Intersect(seg2);
0336         if (embedded != SegmentIntersect::kNoIntersect && embedded != SegmentIntersect::kEmbedding) return false;
0337       }
0338     }
0339     // The center of the circle must not be inside the frame
0340     auto center = trans.Transform(Vector3D<Real_t>{});
0341     if (frame2.Inside(center)) return false;
0342 
0343     return true;
0344   }
0345 
0346   static bool AreDisjoint(RingMask<Real_t> const &frame1, QuadrilateralMask<Real_t> const &frame2,
0347                           TransformationMP<Real_t> const &trans)
0348   {
0349     // Fast check: The safety form outside of the center of the ring in the window frame is larger than the outer radius
0350     auto center = trans.Transform(Vector3D<Real_t>{});
0351     return (frame2.SafetyOutside(center) > frame1.rangeR[1] - vecgeom::kToleranceDist<Real_t> * frame1.rangeR[1]);
0352   }
0353 
0354   static FrameIntersect CheckFrames(RingMask<Real_t> const &f1, QuadrilateralMask<Real_t> const &f2,
0355                                     TransformationMP<Real_t> const &trans)
0356   {
0357     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0358     auto intersect_type = FrameIntersect::kIntersect;
0359     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0360     if (FrameChecker<Real_t, QuadrilateralMask<Real_t>, RingMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0361       if (intersect_type == FrameIntersect::kEmbedding)
0362         intersect_type = FrameIntersect::kEqual;
0363       else
0364         intersect_type = FrameIntersect::kEmbedded;
0365     }
0366     return intersect_type;
0367   }
0368 };
0369 
0370 template <typename Real_t>
0371 struct FrameChecker<Real_t, ZPhiMask<Real_t>, ZPhiMask<Real_t>> {
0372   static bool IsEmbedding(ZPhiMask<Real_t> const &frame1, ZPhiMask<Real_t> const &frame2,
0373                           TransformationMP<Real_t> const &trans)
0374   {
0375     // embedding in Z must be transformed in z
0376     Vector3D<Real_t> zmin(0., 0., frame2.rangeZ[0]);
0377     Vector3D<Real_t> zmax(0., 0., frame2.rangeZ[1]);
0378 
0379     zmin = trans.InverseTransform(zmin);
0380     zmax = trans.InverseTransform(zmax);
0381 
0382     // Z embedding
0383     if (zmin[2] < vecgeom::MakeMinusTolerant<true, Real_t>(frame1.rangeZ[0]) ||
0384         zmax[2] > vecgeom::MakePlusTolerant<true, Real_t>(frame1.rangeZ[1]))
0385       return false;
0386 
0387     // Phi embedding
0388     if (frame1.isFullCirc) return true;
0389     if (frame2.isFullCirc) return false;
0390     Vector3D<Real_t> sPhi{frame2.vecSPhi[0], frame2.vecSPhi[1], 0};
0391     Vector3D<Real_t> ePhi{frame2.vecEPhi[0], frame2.vecEPhi[1], 0};
0392     detail::ConvertAngleToFrame1(sPhi, ePhi, trans);
0393     AngleInterval<Real_t> ang1(frame1.vecSPhi.Phi(), frame1.vecEPhi.Phi());
0394     AngleInterval<Real_t> ang2(sPhi.Phi(), ePhi.Phi());
0395     bool embedding = ang1.Intersect(ang2) == ang2;
0396     return embedding;
0397   }
0398 
0399   static bool AreDisjoint(ZPhiMask<Real_t> const &frame1, ZPhiMask<Real_t> const &frame2,
0400                           TransformationMP<Real_t> const &trans)
0401   {
0402     // embedding in Z must be transformed in z
0403     Vector3D<Real_t> zmin(0., 0., frame2.rangeZ[0]);
0404     Vector3D<Real_t> zmax(0., 0., frame2.rangeZ[1]);
0405 
0406     zmin = trans.InverseTransform(zmin);
0407     zmax = trans.InverseTransform(zmax);
0408 
0409     // Z embedding
0410     if (zmin[2] > vecgeom::MakeMinusTolerant<true, Real_t>(frame1.rangeZ[1]) ||
0411         zmax[2] < vecgeom::MakePlusTolerant<true, Real_t>(frame1.rangeZ[0]))
0412       return true;
0413 
0414     // Check if the masks are phi-disjoint
0415     if (frame1.isFullCirc || frame2.isFullCirc) return false;
0416     // Phi check
0417     Vector3D<Real_t> sPhi{frame2.vecSPhi[0], frame2.vecSPhi[1], 0};
0418     Vector3D<Real_t> ePhi{frame2.vecEPhi[0], frame2.vecEPhi[1], 0};
0419     detail::ConvertAngleToFrame1(sPhi, ePhi, trans);
0420     AngleInterval<Real_t> ang1(frame1.vecSPhi.Phi(), frame1.vecEPhi.Phi());
0421     AngleInterval<Real_t> ang2(sPhi.Phi(), ePhi.Phi());
0422     bool disjoint = ang1.Intersect(ang2).IsNull();
0423     return disjoint;
0424   }
0425 
0426   static FrameIntersect CheckFrames(ZPhiMask<Real_t> const &f1, ZPhiMask<Real_t> const &f2,
0427                                     TransformationMP<Real_t> const &trans)
0428   {
0429     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0430     auto intersect_type = FrameIntersect::kIntersect;
0431     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0432     if (IsEmbedding(f2, f1, trans.Inverse())) {
0433       if (intersect_type == FrameIntersect::kEmbedding)
0434         intersect_type = FrameIntersect::kEqual;
0435       else
0436         intersect_type = FrameIntersect::kEmbedded;
0437     }
0438     return intersect_type;
0439   }
0440 };
0441 
0442 template <typename Real_t>
0443 struct FrameChecker<Real_t, WindowMask<Real_t>, RingMask<Real_t>> {
0444   static bool IsEmbedding(WindowMask<Real_t> const &frame1, RingMask<Real_t> const &frame2,
0445                           TransformationMP<Real_t> const &trans)
0446   {
0447     // Compute SafetyInside for the center of the circle
0448     // NOTE: for now checking only if the full circle is embedded
0449     Vector3D<Real_t> center;
0450     Real_t safety = frame1.SafetyInside(trans.InverseTransform(center));
0451     return safety > frame2.rangeR[1] - vecgeom::kToleranceDist<Real_t> * frame2.rangeR[1];
0452   }
0453 
0454   static bool AreDisjoint(WindowMask<Real_t> const &frame1, RingMask<Real_t> const &frame2,
0455                           TransformationMP<Real_t> const &trans)
0456   {
0457     return FrameChecker<Real_t, RingMask<Real_t>, WindowMask<Real_t>>::AreDisjoint(frame2, frame1, trans.Inverse());
0458   }
0459 
0460   static FrameIntersect CheckFrames(WindowMask<Real_t> const &f1, RingMask<Real_t> const &f2,
0461                                     TransformationMP<Real_t> const &trans)
0462   {
0463     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0464     auto intersect_type = FrameIntersect::kIntersect;
0465     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0466     if (FrameChecker<Real_t, RingMask<Real_t>, WindowMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0467       if (intersect_type == FrameIntersect::kEmbedding)
0468         intersect_type = FrameIntersect::kEqual;
0469       else
0470         intersect_type = FrameIntersect::kEmbedded;
0471     }
0472     return intersect_type;
0473   }
0474 };
0475 
0476 template <typename Real_t>
0477 struct FrameChecker<Real_t, WindowMask<Real_t>, WindowMask<Real_t>> {
0478   static bool IsEmbedding(WindowMask<Real_t> const &frame1, WindowMask<Real_t> const &frame2,
0479                           TransformationMP<Real_t> const &trans)
0480   {
0481     // Convert all vertices of the child in the parent frame, and check if they are contained
0482     Vector3D<Real_t> v[4] = {{frame2.rangeU[0], frame2.rangeV[0], 0},
0483                              {frame2.rangeU[0], frame2.rangeV[1], 0},
0484                              {frame2.rangeU[1], frame2.rangeV[1], 0},
0485                              {frame2.rangeU[1], frame2.rangeV[0], 0}};
0486 
0487     for (auto i = 0; i < 4; ++i) {
0488       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0489     }
0490     return true;
0491   }
0492 
0493   static bool AreDisjoint(WindowMask<Real_t> const &frame1, WindowMask<Real_t> const &frame2,
0494                           TransformationMP<Real_t> const &trans)
0495   {
0496     // Convert all vertices of the child in the parent frame, and check if they are contained
0497     Vector3D<Real_t> v1[4] = {{frame1.rangeU[0], frame1.rangeV[0], 0},
0498                               {frame1.rangeU[0], frame1.rangeV[1], 0},
0499                               {frame1.rangeU[1], frame1.rangeV[1], 0},
0500                               {frame1.rangeU[1], frame1.rangeV[0], 0}};
0501     Vector3D<Real_t> v2[4] = {trans.InverseTransform(Vector3D<Real_t>(frame2.rangeU[0], frame2.rangeV[0], 0)),
0502                               trans.InverseTransform(Vector3D<Real_t>(frame2.rangeU[0], frame2.rangeV[1], 0)),
0503                               trans.InverseTransform(Vector3D<Real_t>(frame2.rangeU[1], frame2.rangeV[1], 0)),
0504                               trans.InverseTransform(Vector3D<Real_t>(frame2.rangeU[1], frame2.rangeV[0], 0))};
0505     Polygon<Real_t, 4> poly1(v1);
0506     Polygon<Real_t, 4> poly2(v2);
0507     auto intersect = poly1.Intersect(poly2);
0508     if (intersect == FrameIntersect::kIntersect) return false;
0509     // Check if the centers are also disjoint
0510     auto center1 = poly1.GetCenter();
0511     auto center2 = poly2.GetCenter();
0512     if (frame1.Inside(Vector3D<Real_t>(center2[0], center2[1], 0)) ||
0513         frame2.Inside(trans.Transform(Vector3D<Real_t>(center1[0], center1[1], 0))))
0514       return false;
0515     return true;
0516   }
0517 
0518   static FrameIntersect CheckFrames(WindowMask<Real_t> const &f1, WindowMask<Real_t> const &f2,
0519                                     TransformationMP<Real_t> const &trans)
0520   {
0521     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0522     auto intersect_type = FrameIntersect::kIntersect;
0523     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0524     if (IsEmbedding(f2, f1, trans.Inverse())) {
0525       if (intersect_type == FrameIntersect::kEmbedding)
0526         intersect_type = FrameIntersect::kEqual;
0527       else
0528         intersect_type = FrameIntersect::kEmbedded;
0529     }
0530     return intersect_type;
0531   }
0532 };
0533 
0534 template <typename Real_t>
0535 struct FrameChecker<Real_t, WindowMask<Real_t>, TriangleMask<Real_t>> {
0536   static bool IsEmbedding(WindowMask<Real_t> const &frame1, TriangleMask<Real_t> const &frame2,
0537                           TransformationMP<Real_t> const &trans)
0538   {
0539     // Convert all vertices of the child in the parent frame, and check if they are contained
0540     Vector3D<Real_t> v[3] = {{frame2.p_[0].x(), frame2.p_[0].y(), 0},
0541                              {frame2.p_[1].x(), frame2.p_[1].y(), 0},
0542                              {frame2.p_[2].x(), frame2.p_[2].y(), 0}};
0543     for (auto i = 0; i < 3; ++i) {
0544       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0545     }
0546     return true;
0547   }
0548 
0549   static bool AreDisjoint(WindowMask<Real_t> const &frame1, TriangleMask<Real_t> const &frame2,
0550                           TransformationMP<Real_t> const &trans)
0551   {
0552     // Convert all vertices of the child in the parent frame, and check if they are contained
0553     Vector3D<Real_t> v1[4] = {{frame1.rangeU[0], frame1.rangeV[0], 0},
0554                               {frame1.rangeU[0], frame1.rangeV[1], 0},
0555                               {frame1.rangeU[1], frame1.rangeV[1], 0},
0556                               {frame1.rangeU[1], frame1.rangeV[0], 0}};
0557     Vector3D<Real_t> v2[3] = {trans.InverseTransform(Vector3D<Real_t>(frame2.p_[0].x(), frame2.p_[0].y(), 0)),
0558                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[1].x(), frame2.p_[1].y(), 0)),
0559                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[2].x(), frame2.p_[2].y(), 0))};
0560     Polygon<Real_t, 4> poly1(v1);
0561     Polygon<Real_t, 3> poly2(v2);
0562     auto intersect = poly1.Intersect(poly2);
0563     if (intersect == FrameIntersect::kIntersect) return false;
0564     // Check if the centers are also disjoint
0565     auto center1 = poly1.GetCenter();
0566     auto center2 = poly2.GetCenter();
0567     if (frame1.Inside(Vector3D<Real_t>(center2[0], center2[1], 0)) ||
0568         frame2.Inside(trans.Transform(Vector3D<Real_t>(center1[0], center1[1], 0))))
0569       return false;
0570     return true;
0571   }
0572 
0573   static FrameIntersect CheckFrames(WindowMask<Real_t> const &f1, TriangleMask<Real_t> const &f2,
0574                                     TransformationMP<Real_t> const &trans)
0575   {
0576     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0577     auto intersect_type = FrameIntersect::kIntersect;
0578     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0579     if (FrameChecker<Real_t, TriangleMask<Real_t>, WindowMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0580       if (intersect_type == FrameIntersect::kEmbedding)
0581         intersect_type = FrameIntersect::kEqual;
0582       else
0583         intersect_type = FrameIntersect::kEmbedded;
0584     }
0585     return intersect_type;
0586   }
0587 };
0588 
0589 template <typename Real_t>
0590 struct FrameChecker<Real_t, WindowMask<Real_t>, QuadrilateralMask<Real_t>> {
0591   static bool IsEmbedding(WindowMask<Real_t> const &frame1, QuadrilateralMask<Real_t> const &frame2,
0592                           TransformationMP<Real_t> const &trans)
0593   {
0594     // Convert all vertices of the child in the parent frame, and check if they are contained
0595     Vector3D<Real_t> v[4] = {{frame2.p_[0].x(), frame2.p_[0].y(), 0},
0596                              {frame2.p_[1].x(), frame2.p_[1].y(), 0},
0597                              {frame2.p_[2].x(), frame2.p_[2].y(), 0},
0598                              {frame2.p_[3].x(), frame2.p_[3].y(), 0}};
0599     for (auto i = 0; i < 4; ++i) {
0600       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0601     }
0602     return true;
0603   }
0604 
0605   static bool AreDisjoint(WindowMask<Real_t> const &frame1, QuadrilateralMask<Real_t> const &frame2,
0606                           TransformationMP<Real_t> const &trans)
0607   {
0608     // Convert all vertices of the child in the parent frame, and check if they are contained
0609     Vector3D<Real_t> v1[4] = {{frame1.rangeU[0], frame1.rangeV[0], 0},
0610                               {frame1.rangeU[0], frame1.rangeV[1], 0},
0611                               {frame1.rangeU[1], frame1.rangeV[1], 0},
0612                               {frame1.rangeU[1], frame1.rangeV[0], 0}};
0613     Vector3D<Real_t> v2[4] = {trans.InverseTransform(Vector3D<Real_t>(frame2.p_[0].x(), frame2.p_[0].y(), 0)),
0614                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[1].x(), frame2.p_[1].y(), 0)),
0615                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[2].x(), frame2.p_[2].y(), 0)),
0616                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[3].x(), frame2.p_[3].y(), 0))};
0617     Polygon<Real_t, 4> poly1(v1);
0618     Polygon<Real_t, 4> poly2(v2);
0619     auto intersect = poly1.Intersect(poly2);
0620     if (intersect == FrameIntersect::kIntersect) return false;
0621     // Check if the centers are also disjoint
0622     auto center1 = poly1.GetCenter();
0623     auto center2 = poly2.GetCenter();
0624     if (frame1.Inside(Vector3D<Real_t>(center2[0], center2[1], 0)) ||
0625         frame2.Inside(trans.Transform(Vector3D<Real_t>(center1[0], center1[1], 0))))
0626       return false;
0627     return true;
0628   }
0629 
0630   static FrameIntersect CheckFrames(WindowMask<Real_t> const &f1, QuadrilateralMask<Real_t> const &f2,
0631                                     TransformationMP<Real_t> const &trans)
0632   {
0633     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0634     auto intersect_type = FrameIntersect::kIntersect;
0635     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0636     if (FrameChecker<Real_t, QuadrilateralMask<Real_t>, WindowMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0637       if (intersect_type == FrameIntersect::kEmbedding)
0638         intersect_type = FrameIntersect::kEqual;
0639       else
0640         intersect_type = FrameIntersect::kEmbedded;
0641     }
0642     return intersect_type;
0643   }
0644 };
0645 
0646 template <typename Real_t>
0647 struct FrameChecker<Real_t, TriangleMask<Real_t>, RingMask<Real_t>> {
0648   static bool IsEmbedding(TriangleMask<Real_t> const &frame1, RingMask<Real_t> const &frame2,
0649                           TransformationMP<Real_t> const &trans)
0650   {
0651     // Compute SafetyInside for the center of the circle
0652     // NOTE: for now checking only if the full circle is embedded
0653     Vector3D<Real_t> center;
0654     Real_t safety = frame1.SafetyInside(trans.InverseTransform(center));
0655     return safety > frame2.rangeR[1] - vecgeom::kToleranceDist<Real_t> * frame2.rangeR[1];
0656   }
0657 
0658   static bool AreDisjoint(TriangleMask<Real_t> const &frame1, RingMask<Real_t> const &frame2,
0659                           TransformationMP<Real_t> const &trans)
0660   {
0661     return FrameChecker<Real_t, RingMask<Real_t>, TriangleMask<Real_t>>::AreDisjoint(frame2, frame1, trans.Inverse());
0662   }
0663 
0664   static FrameIntersect CheckFrames(TriangleMask<Real_t> const &f1, RingMask<Real_t> const &f2,
0665                                     TransformationMP<Real_t> const &trans)
0666   {
0667     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0668     auto intersect_type = FrameIntersect::kIntersect;
0669     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0670     if (FrameChecker<Real_t, RingMask<Real_t>, TriangleMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0671       if (intersect_type == FrameIntersect::kEmbedding)
0672         intersect_type = FrameIntersect::kEqual;
0673       else
0674         intersect_type = FrameIntersect::kEmbedded;
0675     }
0676     return intersect_type;
0677   }
0678 };
0679 
0680 template <typename Real_t>
0681 struct FrameChecker<Real_t, TriangleMask<Real_t>, WindowMask<Real_t>> {
0682   static bool IsEmbedding(TriangleMask<Real_t> const &frame1, WindowMask<Real_t> const &frame2,
0683                           TransformationMP<Real_t> const &trans)
0684   {
0685     // Convert all vertices of the child in the parent frame, and check if they are contained
0686     Vector3D<Real_t> v[4] = {{frame2.rangeU[0], frame2.rangeV[0], 0},
0687                              {frame2.rangeU[0], frame2.rangeV[1], 0},
0688                              {frame2.rangeU[1], frame2.rangeV[1], 0},
0689                              {frame2.rangeU[1], frame2.rangeV[0], 0}};
0690 
0691     for (auto i = 0; i < 4; ++i) {
0692       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0693     }
0694     return true;
0695   }
0696 
0697   static bool AreDisjoint(TriangleMask<Real_t> const &frame1, WindowMask<Real_t> const &frame2,
0698                           TransformationMP<Real_t> const &trans)
0699   {
0700     return FrameChecker<Real_t, WindowMask<Real_t>, TriangleMask<Real_t>>::AreDisjoint(frame2, frame1, trans.Inverse());
0701   }
0702 
0703   static FrameIntersect CheckFrames(TriangleMask<Real_t> const &f1, WindowMask<Real_t> const &f2,
0704                                     TransformationMP<Real_t> const &trans)
0705   {
0706     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0707     auto intersect_type = FrameIntersect::kIntersect;
0708     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0709     if (FrameChecker<Real_t, WindowMask<Real_t>, TriangleMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0710       if (intersect_type == FrameIntersect::kEmbedding)
0711         intersect_type = FrameIntersect::kEqual;
0712       else
0713         intersect_type = FrameIntersect::kEmbedded;
0714     }
0715     return intersect_type;
0716   }
0717 };
0718 
0719 template <typename Real_t>
0720 struct FrameChecker<Real_t, TriangleMask<Real_t>, TriangleMask<Real_t>> {
0721   static bool IsEmbedding(TriangleMask<Real_t> const &frame1, TriangleMask<Real_t> const &frame2,
0722                           TransformationMP<Real_t> const &trans)
0723   {
0724     // Convert all vertices of the child in the parent frame, and check if they are contained
0725     Vector3D<Real_t> v[3] = {{frame2.p_[0].x(), frame2.p_[0].y(), 0},
0726                              {frame2.p_[1].x(), frame2.p_[1].y(), 0},
0727                              {frame2.p_[2].x(), frame2.p_[2].y(), 0}};
0728     for (auto i = 0; i < 3; ++i) {
0729       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0730     }
0731     return true;
0732   }
0733 
0734   static bool AreDisjoint(TriangleMask<Real_t> const &frame1, TriangleMask<Real_t> const &frame2,
0735                           TransformationMP<Real_t> const &trans)
0736   {
0737     // Convert all vertices of the child in the parent frame, and check if they are contained
0738     Vector3D<Real_t> v1[3] = {{frame1.p_[0].x(), frame1.p_[0].y(), 0},
0739                               {frame1.p_[1].x(), frame1.p_[1].y(), 0},
0740                               {frame1.p_[2].x(), frame1.p_[2].y(), 0}};
0741     Vector3D<Real_t> v2[3] = {trans.InverseTransform(Vector3D<Real_t>(frame2.p_[0].x(), frame2.p_[0].y(), 0)),
0742                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[1].x(), frame2.p_[1].y(), 0)),
0743                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[2].x(), frame2.p_[2].y(), 0))};
0744     Polygon<Real_t, 3> poly1(v1);
0745     Polygon<Real_t, 3> poly2(v2);
0746     auto intersect = poly1.Intersect(poly2);
0747     if (intersect == FrameIntersect::kIntersect) return false;
0748     // Check if the centers are also disjoint
0749     auto center1 = poly1.GetCenter();
0750     auto center2 = poly2.GetCenter();
0751     if (frame1.Inside(Vector3D<Real_t>(center2[0], center2[1], 0)) ||
0752         frame2.Inside(trans.Transform(Vector3D<Real_t>(center1[0], center1[1], 0))))
0753       return false;
0754     return true;
0755   }
0756 
0757   static FrameIntersect CheckFrames(TriangleMask<Real_t> const &f1, TriangleMask<Real_t> const &f2,
0758                                     TransformationMP<Real_t> const &trans)
0759   {
0760     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0761     auto intersect_type = FrameIntersect::kIntersect;
0762     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0763     if (IsEmbedding(f2, f1, trans.Inverse())) {
0764       if (intersect_type == FrameIntersect::kEmbedding)
0765         intersect_type = FrameIntersect::kEqual;
0766       else
0767         intersect_type = FrameIntersect::kEmbedded;
0768     }
0769     return intersect_type;
0770   }
0771 };
0772 
0773 template <typename Real_t>
0774 struct FrameChecker<Real_t, TriangleMask<Real_t>, QuadrilateralMask<Real_t>> {
0775   static bool IsEmbedding(TriangleMask<Real_t> const &frame1, QuadrilateralMask<Real_t> const &frame2,
0776                           TransformationMP<Real_t> const &trans)
0777   {
0778     // Convert all vertices of the child in the parent frame, and check if they are contained
0779     Vector3D<Real_t> v[4] = {{frame2.p_[0].x(), frame2.p_[0].y(), 0},
0780                              {frame2.p_[1].x(), frame2.p_[1].y(), 0},
0781                              {frame2.p_[2].x(), frame2.p_[2].y(), 0},
0782                              {frame2.p_[3].x(), frame2.p_[3].y(), 0}};
0783     for (auto i = 0; i < 4; ++i) {
0784       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0785     }
0786     return true;
0787   }
0788 
0789   static bool AreDisjoint(TriangleMask<Real_t> const &frame1, QuadrilateralMask<Real_t> const &frame2,
0790                           TransformationMP<Real_t> const &trans)
0791   {
0792     // Convert all vertices of the child in the parent frame, and check if they are contained
0793     Vector3D<Real_t> v1[3] = {{frame1.p_[0].x(), frame1.p_[0].y(), 0},
0794                               {frame1.p_[1].x(), frame1.p_[1].y(), 0},
0795                               {frame1.p_[2].x(), frame1.p_[2].y(), 0}};
0796     Vector3D<Real_t> v2[4] = {trans.InverseTransform(Vector3D<Real_t>(frame2.p_[0].x(), frame2.p_[0].y(), 0)),
0797                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[1].x(), frame2.p_[1].y(), 0)),
0798                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[2].x(), frame2.p_[2].y(), 0)),
0799                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[3].x(), frame2.p_[3].y(), 0))};
0800     Polygon<Real_t, 3> poly1(v1);
0801     Polygon<Real_t, 4> poly2(v2);
0802     auto intersect = poly1.Intersect(poly2);
0803     if (intersect == FrameIntersect::kIntersect) return false;
0804     // Check if the centers are also disjoint
0805     auto center1 = poly1.GetCenter();
0806     auto center2 = poly2.GetCenter();
0807     if (frame1.Inside(Vector3D<Real_t>(center2[0], center2[1], 0)) ||
0808         frame2.Inside(trans.Transform(Vector3D<Real_t>(center1[0], center1[1], 0))))
0809       return false;
0810     return true;
0811   }
0812 
0813   static FrameIntersect CheckFrames(TriangleMask<Real_t> const &f1, QuadrilateralMask<Real_t> const &f2,
0814                                     TransformationMP<Real_t> const &trans)
0815   {
0816     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0817     auto intersect_type = FrameIntersect::kIntersect;
0818     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0819     if (FrameChecker<Real_t, QuadrilateralMask<Real_t>, TriangleMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0820       if (intersect_type == FrameIntersect::kEmbedding)
0821         intersect_type = FrameIntersect::kEqual;
0822       else
0823         intersect_type = FrameIntersect::kEmbedded;
0824     }
0825     return intersect_type;
0826   }
0827 };
0828 
0829 template <typename Real_t>
0830 struct FrameChecker<Real_t, QuadrilateralMask<Real_t>, RingMask<Real_t>> {
0831   static bool IsEmbedding(QuadrilateralMask<Real_t> const &frame1, RingMask<Real_t> const &frame2,
0832                           TransformationMP<Real_t> const &trans)
0833   {
0834     // Compute SafetyInside for the center of the circle
0835     // NOTE: for now checking only if the full circle is embedded
0836     Vector3D<Real_t> center;
0837     Real_t safety = frame1.SafetyInside(trans.InverseTransform(center));
0838     return safety > frame2.rangeR[1] - vecgeom::kToleranceDist<Real_t> * frame2.rangeR[1];
0839   }
0840 
0841   static bool AreDisjoint(QuadrilateralMask<Real_t> const &frame1, RingMask<Real_t> const &frame2,
0842                           TransformationMP<Real_t> const &trans)
0843   {
0844     return FrameChecker<Real_t, RingMask<Real_t>, QuadrilateralMask<Real_t>>::AreDisjoint(frame2, frame1,
0845                                                                                           trans.Inverse());
0846   }
0847 
0848   static FrameIntersect CheckFrames(QuadrilateralMask<Real_t> const &f1, RingMask<Real_t> const &f2,
0849                                     TransformationMP<Real_t> const &trans)
0850   {
0851     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0852     auto intersect_type = FrameIntersect::kIntersect;
0853     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0854     if (FrameChecker<Real_t, RingMask<Real_t>, QuadrilateralMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0855       if (intersect_type == FrameIntersect::kEmbedding)
0856         intersect_type = FrameIntersect::kEqual;
0857       else
0858         intersect_type = FrameIntersect::kEmbedded;
0859     }
0860     return intersect_type;
0861   }
0862 };
0863 
0864 template <typename Real_t>
0865 struct FrameChecker<Real_t, QuadrilateralMask<Real_t>, WindowMask<Real_t>> {
0866   static bool IsEmbedding(QuadrilateralMask<Real_t> const &frame1, WindowMask<Real_t> const &frame2,
0867                           TransformationMP<Real_t> const &trans)
0868   {
0869     // Convert all vertices of the child in the parent frame, and check if they are contained
0870     Vector3D<Real_t> v[4] = {{frame2.rangeU[0], frame2.rangeV[0], 0},
0871                              {frame2.rangeU[0], frame2.rangeV[1], 0},
0872                              {frame2.rangeU[1], frame2.rangeV[1], 0},
0873                              {frame2.rangeU[1], frame2.rangeV[0], 0}};
0874 
0875     for (auto i = 0; i < 4; ++i) {
0876       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0877     }
0878     return true;
0879   }
0880 
0881   static bool AreDisjoint(QuadrilateralMask<Real_t> const &frame1, WindowMask<Real_t> const &frame2,
0882                           TransformationMP<Real_t> const &trans)
0883   {
0884     return FrameChecker<Real_t, WindowMask<Real_t>, QuadrilateralMask<Real_t>>::AreDisjoint(frame2, frame1,
0885                                                                                             trans.Inverse());
0886   }
0887 
0888   static FrameIntersect CheckFrames(QuadrilateralMask<Real_t> const &f1, WindowMask<Real_t> const &f2,
0889                                     TransformationMP<Real_t> const &trans)
0890   {
0891     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0892     auto intersect_type = FrameIntersect::kIntersect;
0893     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0894     if (FrameChecker<Real_t, WindowMask<Real_t>, QuadrilateralMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0895       if (intersect_type == FrameIntersect::kEmbedding)
0896         intersect_type = FrameIntersect::kEqual;
0897       else
0898         intersect_type = FrameIntersect::kEmbedded;
0899     }
0900     return intersect_type;
0901   }
0902 };
0903 
0904 template <typename Real_t>
0905 struct FrameChecker<Real_t, QuadrilateralMask<Real_t>, TriangleMask<Real_t>> {
0906   static bool IsEmbedding(QuadrilateralMask<Real_t> const &frame1, TriangleMask<Real_t> const &frame2,
0907                           TransformationMP<Real_t> const &trans)
0908   {
0909     // Convert all vertices of the child in the parent frame, and check if they are contained
0910     Vector3D<Real_t> v[3] = {{frame2.p_[0].x(), frame2.p_[0].y(), 0},
0911                              {frame2.p_[1].x(), frame2.p_[1].y(), 0},
0912                              {frame2.p_[2].x(), frame2.p_[2].y(), 0}};
0913     for (auto i = 0; i < 3; ++i) {
0914       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0915     }
0916     return true;
0917   }
0918 
0919   static bool AreDisjoint(QuadrilateralMask<Real_t> const &frame1, TriangleMask<Real_t> const &frame2,
0920                           TransformationMP<Real_t> const &trans)
0921   {
0922     return FrameChecker<Real_t, TriangleMask<Real_t>, QuadrilateralMask<Real_t>>::AreDisjoint(frame2, frame1,
0923                                                                                               trans.Inverse());
0924   }
0925 
0926   static FrameIntersect CheckFrames(QuadrilateralMask<Real_t> const &f1, TriangleMask<Real_t> const &f2,
0927                                     TransformationMP<Real_t> const &trans)
0928   {
0929     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0930     auto intersect_type = FrameIntersect::kIntersect;
0931     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0932     if (FrameChecker<Real_t, TriangleMask<Real_t>, QuadrilateralMask<Real_t>>::IsEmbedding(f2, f1, trans.Inverse())) {
0933       if (intersect_type == FrameIntersect::kEmbedding)
0934         intersect_type = FrameIntersect::kEqual;
0935       else
0936         intersect_type = FrameIntersect::kEmbedded;
0937     }
0938     return intersect_type;
0939   }
0940 };
0941 
0942 template <typename Real_t>
0943 struct FrameChecker<Real_t, QuadrilateralMask<Real_t>, QuadrilateralMask<Real_t>> {
0944   static bool IsEmbedding(QuadrilateralMask<Real_t> const &frame1, QuadrilateralMask<Real_t> const &frame2,
0945                           TransformationMP<Real_t> const &trans)
0946   {
0947     // Convert all vertices of the child in the parent frame, and check if they are contained
0948     Vector3D<Real_t> v[4] = {{frame2.p_[0].x(), frame2.p_[0].y(), 0},
0949                              {frame2.p_[1].x(), frame2.p_[1].y(), 0},
0950                              {frame2.p_[2].x(), frame2.p_[2].y(), 0},
0951                              {frame2.p_[3].x(), frame2.p_[3].y(), 0}};
0952     for (auto i = 0; i < 4; ++i) {
0953       if (!frame1.Inside(trans.InverseTransform(v[i]))) return false;
0954     }
0955     return true;
0956   }
0957 
0958   static bool AreDisjoint(QuadrilateralMask<Real_t> const &frame1, QuadrilateralMask<Real_t> const &frame2,
0959                           TransformationMP<Real_t> const &trans)
0960   {
0961     // Convert all vertices of the child in the parent frame, and check if they are contained
0962     Vector3D<Real_t> v1[4] = {{frame1.p_[0].x(), frame1.p_[0].y(), 0},
0963                               {frame1.p_[1].x(), frame1.p_[1].y(), 0},
0964                               {frame1.p_[2].x(), frame1.p_[2].y(), 0},
0965                               {frame1.p_[3].x(), frame1.p_[3].y(), 0}};
0966     Vector3D<Real_t> v2[4] = {trans.InverseTransform(Vector3D<Real_t>(frame2.p_[0].x(), frame2.p_[0].y(), 0)),
0967                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[1].x(), frame2.p_[1].y(), 0)),
0968                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[2].x(), frame2.p_[2].y(), 0)),
0969                               trans.InverseTransform(Vector3D<Real_t>(frame2.p_[3].x(), frame2.p_[3].y(), 0))};
0970     Polygon<Real_t, 4> poly1(v1);
0971     Polygon<Real_t, 4> poly2(v2);
0972     auto intersect = poly1.Intersect(poly2);
0973     if (intersect == FrameIntersect::kIntersect) return false;
0974     // Check if the centers are also disjoint
0975     auto center1 = poly1.GetCenter();
0976     auto center2 = poly2.GetCenter();
0977     if (frame1.Inside(Vector3D<Real_t>(center2[0], center2[1], 0)) ||
0978         frame2.Inside(trans.Transform(Vector3D<Real_t>(center1[0], center1[1], 0))))
0979       return false;
0980     return true;
0981   }
0982 
0983   static FrameIntersect CheckFrames(QuadrilateralMask<Real_t> const &f1, QuadrilateralMask<Real_t> const &f2,
0984                                     TransformationMP<Real_t> const &trans)
0985   {
0986     if (AreDisjoint(f1, f2, trans)) return FrameIntersect::kNoIntersect;
0987     auto intersect_type = FrameIntersect::kIntersect;
0988     if (IsEmbedding(f1, f2, trans)) intersect_type = FrameIntersect::kEmbedding;
0989     if (IsEmbedding(f2, f1, trans.Inverse())) {
0990       if (intersect_type == FrameIntersect::kEmbedding)
0991         intersect_type = FrameIntersect::kEqual;
0992       else
0993         intersect_type = FrameIntersect::kEmbedded;
0994     }
0995     return intersect_type;
0996   }
0997 };
0998 
0999 /// More FrameChecker specializations follow
1000 
1001 } // namespace vgbrep
1002 
1003 #endif