/src/s2geometry/src/s2/internal/s2incident_edge_tracker.h
Line | Count | Source |
1 | | // Copyright Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS-IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | |
16 | | #ifndef S2_INTERNAL_S2INCIDENT_EDGE_TRACKER_H_ |
17 | | #define S2_INTERNAL_S2INCIDENT_EDGE_TRACKER_H_ |
18 | | |
19 | | #include <algorithm> |
20 | | #include <cstdint> |
21 | | #include <utility> |
22 | | #include <vector> |
23 | | |
24 | | #include "absl/container/btree_map.h" |
25 | | #include "absl/container/flat_hash_set.h" |
26 | | #include "s2/_fp_contract_off.h" // IWYU pragma: keep |
27 | | #include "s2/s2point.h" |
28 | | #include "s2/s2shape.h" |
29 | | |
30 | | namespace internal { |
31 | | |
32 | | // A tuple of (shape id, vertex) that compares by shape id. |
33 | | struct IncidentEdgeKey { |
34 | | IncidentEdgeKey() = default; |
35 | | |
36 | | explicit IncidentEdgeKey(int shape_id) |
37 | 0 | : shape_id(shape_id) { /* Leave vertex undefined */ |
38 | 0 | } |
39 | | |
40 | | IncidentEdgeKey(int shape_id, S2Point vertex) |
41 | 0 | : shape_id(shape_id), vertex(vertex) {} |
42 | | |
43 | | int32_t shape_id; |
44 | | S2Point vertex; |
45 | | |
46 | | // We need a strict ordering to be a valid key for an ordered container, but |
47 | | // we don't actually care about the ordering of the vertices (as long as |
48 | | // they're grouped by shape id). Vertices are 3D points so they don't have a |
49 | | // natural ordering, so we'll just compare them lexicographically. |
50 | 0 | bool operator<(const IncidentEdgeKey& b) const { |
51 | 0 | if (shape_id < b.shape_id) return true; |
52 | 0 | if (shape_id > b.shape_id) return false; |
53 | 0 | for (int i = 0; i < 3; ++i) { |
54 | 0 | if (vertex[i] < b.vertex[i]) return true; |
55 | 0 | if (vertex[i] > b.vertex[i]) return false; |
56 | 0 | } |
57 | 0 | return false; // Must be equal. |
58 | 0 | } |
59 | | }; |
60 | | |
61 | | // Map from keys of (shape id, vertex) to a set of edge_ids. We can and do |
62 | | // encounter the same edges multiple times, so we need to use a set to |
63 | | // deduplicate edges as they're inserted. |
64 | | using IncidentEdgeSet = |
65 | | absl::btree_map<IncidentEdgeKey, absl::flat_hash_set<int32_t>>; |
66 | | |
67 | | // A class for detecting and tracking shape edges that are incident on the same |
68 | | // vertex. Edges of multiple shapes may be tracked, but lookup is by shape id |
69 | | // and vertex: there is no facility to get all edges of all shapes at a vertex. |
70 | | // Edge vertices must compare exactly equal to be considered the same vertex, no |
71 | | // tolerance is applied as this isn't intended for e.g.: snapping shapes |
72 | | // together, which S2Builder does better and more robustly. |
73 | | // |
74 | | // To use, instantiate and then add edges with one or more sequences of calls, |
75 | | // where each sequence begins with StartShape(), followed by AddEdge() calls to |
76 | | // add edges for that shape, and ends with FinishShape(). Those sequences do |
77 | | // not need to visit shapes or edges in order. Then, call incidentEdges() to get |
78 | | // the resulting map from IncidentEdgeKeys (which are shapeId, vertex pairs) to |
79 | | // a set of edgeIds of the shape that are incident to that vertex.. |
80 | | // |
81 | | // This works on a block of edges at a time, meaning that to detect incident |
82 | | // edges on a particular vertex, we must have at least three edges incident |
83 | | // at that vertex when FinishShape() is called. We don't maintain partial |
84 | | // information between calls. However, subject to this constraint, a single |
85 | | // shape's edges may be defined with multiple sequences of StartShape(), |
86 | | // AddEdge()... , FinishShape() calls. |
87 | | // |
88 | | // The reason for this is simple: most edges don't have more than two incident |
89 | | // edges (the incoming and outgoing edge). If we had to maintain information |
90 | | // between calls, we'd end up with a map that contains every vertex, to no |
91 | | // benefit. Instead, when FinishShape() is called, we discard vertices that |
92 | | // contain two or fewer incident edges. |
93 | | // |
94 | | // In principle this isn't a real limitation because generally we process an |
95 | | // S2ShapeIndex cell at a time, and, if a vertex has multiple edges, we'll see |
96 | | // all the edges in the same cell as the vertex, and, in general, it's possible |
97 | | // to aggregate edges before calling. |
98 | | // |
99 | | // The tracker maintains incident edges until it's cleared. If you call it with |
100 | | // each cell from an S2ShapeIndex, then at the end you will have all the |
101 | | // incident edge information for the whole index. If only a subset is needed, |
102 | | // call Reset() when you're done. |
103 | | class S2IncidentEdgeTracker { |
104 | | public: |
105 | | // Add edges to the edge tracker. After calling, any vertices with multiple |
106 | | // (> 2) incident edges will appear in the incident edge map. |
107 | | void StartShape(int shape_id); |
108 | | void AddEdge(int edge_id, const S2Shape::Edge& edge); |
109 | | void FinishShape(); |
110 | | |
111 | | // Clear any accumulated state. |
112 | 0 | void Reset() { incident_edge_map_.clear(); } |
113 | | |
114 | | // Returns a const reference to the incident edge map. |
115 | 0 | const IncidentEdgeSet& IncidentEdges() const { return incident_edge_map_; } |
116 | | |
117 | | private: |
118 | | // Scratch space to temporarily store incident edges in. Having a separate |
119 | | // space for these lets us remove vertices with only one incident edge |
120 | | // easily. |
121 | | struct VertexEdge { |
122 | | S2Point vertex; |
123 | | int32_t edge_id; |
124 | | }; |
125 | | std::vector<VertexEdge> nursery_; |
126 | | |
127 | | int current_shape_ = -1; |
128 | | |
129 | | // Actual incident edge map. |
130 | | IncidentEdgeSet incident_edge_map_; |
131 | | }; |
132 | | |
133 | | } // namespace internal |
134 | | |
135 | | #endif // S2_INTERNAL_S2INCIDENT_EDGE_TRACKER_H_ |