LCOV - code coverage report
Current view: top level - core/tess - SPTessTypes.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 36 39 92.3 %
Date: 2024-05-12 00:16:13 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #ifndef STAPPLER_TESS_SPTESSTYPES_H_
      25             : #define STAPPLER_TESS_SPTESSTYPES_H_
      26             : 
      27             : #include "SPVec4.h"
      28             : #include "SPTess.h"
      29             : 
      30             : namespace STAPPLER_VERSIONIZED stappler::geom {
      31             : 
      32             : struct Vertex;
      33             : struct FaceEdge;
      34             : struct HalfEdge;
      35             : struct Edge;
      36             : 
      37             : using QueueHandle = int32_t;
      38             : 
      39             : static constexpr uint32_t VertexSetPrealloc = 64;
      40             : static constexpr uint32_t EdgeSetPrealloc = 64;
      41             : static constexpr uint32_t VertexAllocBatch = 32;
      42             : static constexpr uint32_t EdgeAllocBatch = 32;
      43             : 
      44             : extern int TessVerboseInfo;
      45             : 
      46             : enum class VertexType {
      47             :         Start, // right non-convex angle
      48             :         End, // left non-convex angle
      49             :         Split, // right convex angle
      50             :         Merge, // left convex angle
      51             :         RegularTop, // boundary below vertex
      52             :         RegularBottom, // boundary above vertex
      53             : };
      54             : 
      55             : struct Helper {
      56             :         HalfEdge *e1 = nullptr;
      57             :         HalfEdge *e2 = nullptr;
      58             :         VertexType type = VertexType::Start;
      59             : };
      60             : 
      61             : struct EdgeDictNode {
      62             :         Vec2 org;
      63             :         Vec2 norm;
      64             :         mutable Vec4 value; // value, dst
      65             :         Edge *edge = nullptr;
      66             :         int16_t windingAbove = 0;
      67             :         bool horizontal = false;
      68             : 
      69             :         mutable Helper helper;
      70             : 
      71   209130958 :         Vec2 current() const { return Vec2(value.x, value.y); }
      72   209165344 :         Vec2 dst() const { return Vec2(value.z, value.w); }
      73             :         float dstX() const { return value.z; }
      74             :         float dstY() const { return value.w; }
      75             : 
      76             :         bool operator < (const EdgeDictNode &other) const;
      77             :         bool operator < (const Edge &other) const;
      78             :         bool operator < (const Vec2 &other) const;
      79             :         bool operator <= (const EdgeDictNode &other) const;
      80             :         bool operator== (const EdgeDictNode &other) const;
      81             : };
      82             : 
      83             : struct Vertex {
      84             :         HalfEdge *_edge = nullptr;  /* a half-edge with this origin */
      85             :         Vec2 _origin;
      86             :         uint32_t _uniqueIdx = maxOf<uint32_t>(); /* to allow identify unique vertices */
      87             :         QueueHandle _queueHandle = maxOf<QueueHandle>(); /* to allow deletion from priority queue */
      88             :         uint32_t _exportIdx = maxOf<uint32_t>();
      89             : 
      90             :         void insertBefore(HalfEdge *eOrig);
      91             :         void removeFromList(Vertex *newOrg);
      92             : 
      93             :         void foreach(const Callback<void(const HalfEdge &)> &) const;
      94             : 
      95             :         void relocate(const Vec2 &);
      96             : };
      97             : 
      98             : struct FaceEdge : memory::AllocPool {
      99             :         FaceEdge *_next = nullptr;
     100             :         Vertex *_vertex = nullptr;
     101             :         Vec2 _origin;
     102             :         Vec2 _displaced;
     103             :         float _value = 0.0f;
     104             :         float _direction = 0.0f;
     105             :         bool _splitVertex = false;
     106             :         bool _degenerate = false;
     107             : 
     108             :         void foreach(const Callback<void(const FaceEdge &)> &) const;
     109             : };
     110             : 
     111             : struct HalfEdge {
     112             :         HalfEdge *_originNext = nullptr; /* next edge CCW around origin */
     113             :         HalfEdge *_leftNext = nullptr; /* next edge CCW around left face */
     114             :         Vec2 origin;
     115             :         uint32_t vertex = maxOf<uint32_t>(); // normally, we should not access vertex directly to improve data locality
     116             :         int16_t _realWinding = 0;
     117             :         int16_t isRight : 2 = 0; // -1 or 1
     118             :         int16_t edgeOffset : 2 = 0; // 0 or 1
     119             :         int16_t _winding : 2 = 0; /* change in winding number when crossing from the right face to the left face */
     120             :         int16_t _mark : 10 = 0;
     121             : 
     122             :         static void splitEdgeLoops(HalfEdge *eOrg, HalfEdge *eNew, Vertex *v);
     123             :         static void joinEdgeLoops(HalfEdge *eOrg, HalfEdge *oPrev);
     124             : 
     125             :         HalfEdge *sym() const; // uses `this` pointer and isLeft to find Sym edge
     126             : 
     127             :         uint32_t getIndex() const;
     128             : 
     129             :         void setOrigin(const Vertex *);
     130             :         void copyOrigin(const HalfEdge *);
     131             : 
     132             :         HalfEdge *getOriginNext() const;
     133             :         HalfEdge *getOriginPrev() const;
     134             : 
     135             :         HalfEdge *getDestinationNext() const;
     136             :         HalfEdge *getDestinationPrev() const;
     137             : 
     138             :         HalfEdge *getLeftLoopNext() const;
     139             :         HalfEdge *getLeftLoopPrev() const;
     140             : 
     141             :         HalfEdge *getRightLoopNext() const;
     142             :         HalfEdge *getRightLoopPrev() const;
     143             : 
     144             :         const Vec2 &getOrgVec() const;
     145             :         const Vec2 &getDstVec() const;
     146    12190757 :         Vec2 getNormVec() const { return getDstVec() - getOrgVec(); }
     147             : 
     148             :         float getLength() const;
     149             : 
     150             :         Edge *getEdge() const;
     151             : 
     152             :         // edge info should be updated
     153             :         bool goesLeft() const; // right edge goes left
     154             :         bool goesRight() const; // left edge goes right
     155             : 
     156             :         void foreachOnFace(const Callback<void(HalfEdge &)> &);
     157             :         void foreachOnVertex(const Callback<void(HalfEdge &)> &);
     158             : 
     159             :         void foreachOnFace(const Callback<void(const HalfEdge &)> &) const;
     160             :         void foreachOnVertex(const Callback<void(const HalfEdge &)> &) const;
     161             : 
     162             :         float getDirection() const;
     163             : };
     164             : 
     165             : struct Edge {
     166             :         HalfEdge left;
     167             :         HalfEdge right;
     168             :         const EdgeDictNode *node = nullptr;
     169             :         float direction = nan();
     170             :         bool inverted = false; // inverted means left edge goes right
     171             :         bool invalidated = false;
     172             : 
     173             :         Edge();
     174             : 
     175             :         const Vec2 &getLeftVec() const;
     176             :         const Vec2 &getRightVec() const;
     177             : 
     178             :         const Vec2 &getOrgVec() const;
     179             :         const Vec2 &getDstVec() const;
     180             : 
     181             :         uint32_t getLeftOrg() const;
     182             :         uint32_t getRightOrg() const;
     183             : 
     184             :         void updateInfo();
     185             : 
     186             :         int16_t getLeftWinding() const;
     187             :         int16_t getRightWinding() const;
     188             : 
     189             :         // halfedge in positive sweep direction
     190        1449 :         HalfEdge *getPostitive() { return (inverted) ? &right : &left; }
     191             : 
     192             :         // halfedge in negative sweep direction
     193         241 :         HalfEdge *getNegative() { return (inverted) ? &left : &right; }
     194             : };
     195             : 
     196             : struct ObjectAllocator : public memory::AllocPool {
     197             :         memory::pool_t *_pool = nullptr;
     198             : 
     199             :         Vertex *_freeVertexes = nullptr;
     200             :         Edge *_freeEdges = nullptr;
     201             :         FaceEdge *_freeFaces = nullptr;
     202             : 
     203             :         memory::vector<Vertex *> _vertexes;
     204             :         memory::vector<Vertex *> _exportVertexes;
     205             :         memory::vector<HalfEdge *> _edgesOfInterests;
     206             :         memory::vector<HalfEdge *> _faceEdges;
     207             : 
     208             :         memory::vector<FaceEdge *> _boundaries;
     209             : 
     210             :         uint32_t _vertexOffset = 0;
     211             : 
     212             :         ObjectAllocator(memory::pool_t *pool);
     213             : 
     214             :         Edge *allocEdge();
     215             :         Vertex *allocVertex();
     216             :         FaceEdge *allocFaceEdge();
     217             : 
     218             :         void releaseEdge(Edge *);
     219             :         void releaseVertex(uint32_t, uint32_t);
     220             :         void releaseVertex(Vertex *);
     221             :         void trimVertexes();
     222             : 
     223             :         void preallocateVertexes(uint32_t n);
     224             :         void preallocateEdges(uint32_t n);
     225             :         void preallocateFaceEdges(uint32_t n);
     226             : 
     227             :         void removeEdgeFromVec(memory::vector<HalfEdge *> &, HalfEdge *);
     228             : };
     229             : 
     230             : struct VertexPriorityQueue {
     231             :         using Handle = QueueHandle;
     232             :         using Key = Vertex *;
     233             : 
     234             :         static constexpr Handle InvalidHandle = maxOf<Handle>();
     235             : 
     236             :         struct Node {
     237             :                 Handle handle;
     238             :         };
     239             : 
     240             :         struct Elem {
     241             :                 Key key;
     242             :                 Handle node;
     243             :         };
     244             : 
     245             :         struct Heap {
     246             :                 Node *nodes = nullptr;
     247             :                 Elem *handles = nullptr;
     248             :                 uint32_t size = 0, max = 0;
     249             :                 Handle freeList = 0;
     250             :                 bool initialized = false;
     251             :                 memory::pool_t *pool;
     252             : 
     253             :                 Heap(memory::pool_t *, uint32_t);
     254             :                 ~Heap();
     255             : 
     256             :                 void init();
     257    17996181 :                 bool empty() const { return size == 0; }
     258      576760 :                 Key getMin() const { return handles[nodes[1].handle].key; }
     259             : 
     260             :                 Handle insert(Key keyNew);
     261             :                 Key extractMin();
     262             :                 void remove(Handle hCurr);
     263             : 
     264             :                 void floatDown(int curr);
     265             :                 void floatUp(int curr);
     266             :         };
     267             : 
     268             :         Heap heap;
     269             :         Key *keys = nullptr;
     270             :         Key **order = nullptr;
     271             :         uint32_t size = 0, max = 0;
     272             :         bool initialized = false;
     273             :         memory::pool_t *pool = nullptr;
     274             :         Handle freeList = 0;
     275             : 
     276             :         VertexPriorityQueue(memory::pool_t *, const memory::vector<Vertex *> &);
     277             :         ~VertexPriorityQueue();
     278             : 
     279             :         bool init();
     280             : 
     281             :         bool empty() const;
     282             : 
     283             :         Handle insert(Key);
     284             :         void remove(Handle);
     285             : 
     286             :         Key extractMin();
     287             :         Key getMin() const;
     288             : };
     289             : 
     290             : enum class IntersectionEvent {
     291             :         Regular,
     292             :         EventIsIntersection, // intersection directly on event point, new edge should split old one
     293             :         EdgeConnection1, // connection, ends on old edge
     294             :         EdgeConnection2, // connection, ends on new edge
     295             : };
     296             : 
     297             : struct EdgeDict {
     298             :         Vec2 event;
     299             : 
     300             :         memory::set<EdgeDictNode> nodes;
     301             :         memory::pool_t *pool = nullptr;
     302             : 
     303             :         EdgeDict(memory::pool_t *, uint32_t size);
     304             : 
     305             :         const EdgeDictNode * push(Edge *, int16_t windingAbove);
     306             :         void pop(const EdgeDictNode *);
     307             : 
     308             :         void update(Vertex *, float tolerance);
     309             : 
     310             :         const EdgeDictNode * checkForIntersects(Vertex *, Vec2 &, IntersectionEvent &, float tolerance) const;
     311             :         const EdgeDictNode * checkForIntersects(HalfEdge *, Vec2 &, IntersectionEvent &, float tolerance) const;
     312             : 
     313             :         // find edge directly below edge (used for region winding)
     314             :         const EdgeDictNode * getEdgeBelow(const Edge *) const;
     315             : 
     316             :         // find edge directly below point (used for monotonize algo)
     317             :         const EdgeDictNode * getEdgeBelow(const Vec2 &, uint32_t vertex) const;
     318             : };
     319             : 
     320             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool VertLeq(const Vec2 &u, const Vec2 &v) {
     321    35843010 :         return ((u.x < v.x) || (u.x == v.x && u.y <= v.y));
     322             : }
     323             : 
     324             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool VertLeq(const Vertex *u, const Vertex *v) {
     325    82759688 :         return ((u->_origin.x < v->_origin.x) || (u->_origin.x == v->_origin.x && u->_origin.y <= v->_origin.y));
     326             : }
     327             : 
     328             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool VertEq(const Vec2 &u, const Vec2 &v, float tolerance) {
     329   414153811 :         return u.fuzzyEquals(v, tolerance);
     330             : }
     331             : 
     332             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool FloatEq(float u, float v, float tolerance) {
     333    46990303 :         return u - tolerance <= v && v <= u + tolerance;
     334             : }
     335             : 
     336             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool VertEq(const Vertex *u, const Vertex *v, float tolerance) {
     337     9127899 :         return VertEq(u->_origin, v->_origin, tolerance);
     338             : }
     339             : 
     340             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool EdgeGoesRight(const HalfEdge *e) {
     341    18259756 :         return VertLeq(e->origin, e->sym()->origin);
     342             : }
     343             : 
     344             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool EdgeGoesLeft(const HalfEdge *e) {
     345             :         return !VertLeq(e->origin, e->sym()->origin);
     346             : }
     347             : 
     348             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool AngleIsConvex(const HalfEdge *a, const HalfEdge *b) {
     349     8021042 :         return a->getEdge()->direction > b->getEdge()->direction;
     350             : }
     351             : 
     352             : 
     353             : // fast synthetic tg|ctg function, returns range [-2.0, 2.0f]
     354             : // which monotonically grows with angle between vec and 0x as argument;
     355             : // norm.x assumed to be positive
     356             : SP_ATTR_OPTIMIZE_INLINE_FN static inline float EdgeDirection(const Vec2 &norm) {
     357    18332951 :         if (norm.y >= 0) {
     358     9966303 :                 return (norm.x > norm.y) ? (norm.y / norm.x) : (2.0f - norm.x / norm.y);
     359             :         } else {
     360     8366648 :                 return (norm.x > -norm.y) ? (norm.y / norm.x) : (-2.0f - norm.x / norm.y);
     361             :         }
     362             : }
     363             : 
     364             : // same method, map full angle with positive x axis to [0.0f, 8.0f)
     365             : SP_ATTR_OPTIMIZE_INLINE_FN static inline float EdgeAngle(const Vec2 &norm) {
     366     9988472 :         if (norm.x >= 0 && norm.y >= 0) {
     367             :                 // [0.0, 2.0]
     368     3531014 :                 return (norm.x > norm.y) ? (norm.y / norm.x) : (2.0f - norm.x / norm.y);
     369     6457458 :         } else if (norm.x < 0 && norm.y >= 0) {
     370             :                 // (2.0, 4.0]
     371     2049762 :                 return (-norm.x > norm.y) ? (4.0 + norm.y / norm.x) : (2.0f - norm.x / norm.y);
     372     4407696 :         } else if (norm.x < 0 && norm.y < 0) {
     373             :                 // (4.0, 6.0)
     374     1988392 :                 return (norm.x < norm.y) ? (4.0 + norm.y / norm.x) : (6.0f - norm.x / norm.y);
     375             :         } else {
     376             :                 // [6.0, 8.0)
     377     2419304 :                 return (norm.x > -norm.y) ? (8.0 + norm.y / norm.x) : (6.0f - norm.x / norm.y);
     378             :         }
     379             : }
     380             : 
     381             : SP_ATTR_OPTIMIZE_INLINE_FN static inline float EdgeAngle(const Vec2 &from, const Vec2 &to) {
     382     6095568 :         if (from == to) {
     383             :                 return 8.0f;
     384             :         }
     385             : 
     386             :         auto fromA = EdgeAngle(from);
     387             :         auto toA = EdgeAngle(to);
     388             : 
     389     4994236 :         if (std::isnan(fromA) || std::isnan(toA)) {
     390         525 :                 std::cerr << "EdgeAngle (NaN): " << from << " " << to << "\n";
     391         525 :                 return 0.0f;
     392             :         }
     393             : 
     394     4993689 :         if (fromA <= toA) {
     395     2949672 :                 return toA - fromA;
     396             :         } else {
     397     2044017 :                 return 8.0 - (fromA - toA);
     398             :         }
     399             : }
     400             : 
     401             : SP_ATTR_OPTIMIZE_INLINE_FN static inline bool EdgeAngleIsBelowTolerance(float A, float tolerance) {
     402     3738355 :         return A < tolerance || 8.0f - A < tolerance;
     403             : }
     404             : 
     405             : std::ostream &operator<<(std::ostream &out, const Vertex &v);
     406             : std::ostream &operator<<(std::ostream &out, const HalfEdge &e);
     407             : std::ostream &operator<<(std::ostream &out, const FaceEdge &e);
     408             : std::ostream &operator<<(std::ostream &out, VerboseFlag e);
     409             : std::ostream &operator<<(std::ostream &out, const EdgeDictNode &e);
     410             : std::ostream &operator<<(std::ostream &out, const Edge &e);
     411             : 
     412             : inline std::ostream &
     413             : operator << (std::ostream & os, const IntersectionEvent & ev) {
     414             :         switch (ev) {
     415             :         case IntersectionEvent::Regular: os << "Regular"; break;
     416             :         case IntersectionEvent::EventIsIntersection: os << "EventIsIntersection"; break;
     417             :         case IntersectionEvent::EdgeConnection1: os << "EdgeConnection1"; break;
     418             :         case IntersectionEvent::EdgeConnection2: os << "EdgeConnection2"; break;
     419             :         }
     420             :         return os;
     421             : }
     422             : 
     423    34070519 : inline bool isWindingInside(Winding w, int16_t n) {
     424    34070519 :         switch (w) {
     425     8897199 :         case Winding::EvenOdd: return (n & 1); break;
     426    25173320 :         case Winding::NonZero: return (n != 0); break;
     427           0 :         case Winding::Positive: return (n > 0); break;
     428           0 :         case Winding::Negative: return (n < 0); break;
     429           0 :         case Winding::AbsGeqTwo: return (n >= 2) || (n <= -2); break;
     430             :         }
     431             :         return false;
     432             : }
     433             : 
     434             : }
     435             : 
     436             : #endif /* STAPPLER_TESS_SPTESSTYPES_H_ */

Generated by: LCOV version 1.14