CARLA
DoublyConnectedEdgeList.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
2 // de Barcelona (UAB).
3 //
4 // This work is licensed under the terms of the MIT license.
5 // For a copy, see <https://opensource.org/licenses/MIT>.
6 
7 #include "Carla.h"
9 
10 #include <cmath>
11 #include <map>
12 
13 #ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
14 #include <sstream>
15 #endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
16 
17 namespace MapGen {
18 
19  // ===========================================================================
20  // -- Local static methods ---------------------------------------------------
21  // ===========================================================================
22 
23 #ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
24 
25  static void ResetIndices() {
26  GraphNode::ResetIndex();
27  GraphHalfEdge::ResetIndex();
28  GraphFace::ResetIndex();
29  }
30 
31 #endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
32 
33  /// Return the pair {prev, next}, where prev/next is the previous/next edge
34  /// counterclockwise around edge's source node. I.e., edge's position is in
35  /// between prev and next.
36  ///
37  /// Note: Always returns the half-edge pointing out from node.
38  ///
39  /// The time complexity is O(n*log(n)) where n is the number of edges of
40  /// edge's source.
41  static std::pair<DoublyConnectedEdgeList::HalfEdge *, DoublyConnectedEdgeList::HalfEdge *>
43  {
44  using Dcel = DoublyConnectedEdgeList;
45  // from [-pi, pi] to [0, 1].
46  auto normalize = [](auto a) {
47  constexpr float twoPi = 2.0 * 3.14159265359;
48  a /= twoPi;
49  while (a >= 1.0) a -= 1.0;
50  while (a < 0.0) a += 1.0;
51  return a;
52  };
53  auto angle = Dcel::GetAngle(halfEdge);
54  std::map<decltype(angle), Dcel::HalfEdge *> edgeMap;
55  // Iterate every half-edge in the source node.
56  auto &firstHalfEdge = Dcel::GetLeavingHalfEdge(Dcel::GetSource(halfEdge));
57  auto *edge = &firstHalfEdge;
58  do {
59  if (edge != &halfEdge) {
60  auto alpha = DoublyConnectedEdgeList::GetAngle(*edge);
61  auto a = normalize(alpha - angle);
62  edgeMap.insert({a, edge});
63  }
64  edge = &Dcel::GetNextInNode(*edge);
65  } while (edge != &firstHalfEdge);
66  check(!edgeMap.empty());
67  return {edgeMap.rbegin()->second, edgeMap.begin()->second};
68  }
69 
70  // ===========================================================================
71  // -- Constructors and destructor --------------------------------------------
72  // ===========================================================================
73 
75  const Position &Position0,
76  const Position &Position1) :
77  Nodes(),
78  HalfEdges(2u),
79  Faces(1u)
80  {
81  Nodes.emplace_back(Position0);
82  Nodes.emplace_back(Position1);
83 
84  Faces.front().HalfEdge = &HalfEdges.front();
85 
86  HalfEdges.front().Source = &Nodes.front();
87  HalfEdges.front().Target = &Nodes.back();
88  HalfEdges.front().Next = &HalfEdges.back();
89  HalfEdges.front().Pair = &HalfEdges.back();
90  HalfEdges.front().Face = &Faces.front();
91 
92  HalfEdges.back().Source = &Nodes.back();
93  HalfEdges.back().Target = &Nodes.front();
94  HalfEdges.back().Next = &HalfEdges.front();
95  HalfEdges.back().Pair = &HalfEdges.front();
96  HalfEdges.back().Face = &Faces.front();
97 
98  Nodes.front().LeavingHalfEdge = &HalfEdges.front();
99  Nodes.back().LeavingHalfEdge = &HalfEdges.back();
100  }
101 
103  {
104 #ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
105  ResetIndices();
106 #endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
107  }
108 
109  // ===========================================================================
110  // -- Adding elements to the graph -------------------------------------------
111  // ===========================================================================
112 
114  const Position &NodePosition,
115  Node &OtherNode)
116  {
117  Nodes.emplace_back(NodePosition);
118  auto &newNode = Nodes.back();
119  HalfEdges.emplace_back();
120  auto &edge0 = HalfEdges.back();
121  HalfEdges.emplace_back();
122  auto &edge1 = HalfEdges.back();
123 
124  edge0.Source = &newNode;
125  edge0.Target = &OtherNode;
126  edge0.Pair = &edge1;
127 
128  edge1.Source = &OtherNode;
129  edge1.Target = &newNode;
130  edge1.Pair = &edge0;
131 
132  HalfEdge *prev;
133  HalfEdge *next;
134  std::tie(prev, next) = FindPositionInNode(edge1);
135 
136  edge0.Next = next;
137  edge0.Face = next->Face;
138 
139  edge1.Next = &edge0;
140  edge1.Face = next->Face;
141 
142  prev->Pair->Next = &edge1;
143 
144  newNode.LeavingHalfEdge = &edge0;
145  return newNode;
146  }
147 
149  const Position &Position,
150  HalfEdge &edge)
151  {
152  HalfEdges.emplace_back();
153  auto &edge0 = HalfEdges.back();
154  HalfEdges.emplace_back();
155  auto &edge1 = HalfEdges.back();
156 
157  Nodes.emplace_back(Position);
158  auto &newNode = Nodes.back();
159 
160  auto &node0 = *edge.Source;
161 
162  // Opposite direction of edge.
163  edge0.Source = &newNode;
164  edge0.Target = &node0;
165  edge0.Pair = &edge1;
166  edge0.Face = edge.Pair->Face;
167 
168  // Same direction as edge.
169  edge1.Source = &node0;
170  edge1.Target = &newNode;
171  edge1.Pair = &edge0;
172  edge1.Next = &edge;
173  edge1.Face = edge.Face;
174 
175  // Fix connections to node0.
176  HalfEdge *prev;
177  HalfEdge *next;
178  std::tie(prev, next) = FindPositionInNode(edge);
179  edge0.Next = next;
180  prev->Pair->Next = &edge1;
181 
182  // Fix the pair that was split.
183  edge.Source = &newNode;
184  edge.Pair->Target = &newNode;
185  edge.Pair->Next = &edge0;
186 
187  // Fix the node's edges.
188  node0.LeavingHalfEdge = &edge1;
189  newNode.LeavingHalfEdge = &edge0;
190 
191  return newNode;
192  }
193 
195  Node &Node0,
196  Node &Node1)
197  {
198  Faces.emplace_back();
199  auto &newFace = Faces.back();
200  HalfEdges.emplace_back();
201  auto &edge0 = HalfEdges.back();
202  HalfEdges.emplace_back();
203  auto &edge1 = HalfEdges.back();
204 
205  edge0.Source = &Node0;
206  edge0.Target = &Node1;
207  edge0.Pair = &edge1;
208  edge1.Source = &Node1;
209  edge1.Target = &Node0;
210  edge1.Pair = &edge0;
211 
212  // Connect edges to node0.
213  HalfEdge *prev0;
214  HalfEdge *next0;
215  std::tie(prev0, next0) = FindPositionInNode(edge0);
216  edge1.Next = next0;
217  prev0->Pair->Next = &edge0;
218 
219  // Connect edges to node1.
220  HalfEdge *prev1;
221  HalfEdge *next1;
222  std::tie(prev1, next1) = FindPositionInNode(edge1);
223  edge0.Next = next1;
224  prev1->Pair->Next = &edge1;
225 
226  // Attach faces to the newly created edges.
227  auto &oldFace = *next1->Face;
228  oldFace.HalfEdge = &edge0;
229  newFace.HalfEdge = &edge1;
230 
231  // Iterate over the edges of each face and correct pointers.
232  auto fixFace = [](Face &face) {
233  auto &firstEdge = GetHalfEdge(face);
234  auto *edge = &firstEdge;
235  do {
236  edge->Face = &face;
237  edge = &GetNextInFace(*edge);
238  } while (edge != &firstEdge);
239  };
240  fixFace(oldFace);
241  fixFace(newFace);
242 
243  return newFace;
244  }
245 
246  // ===========================================================================
247  // -- Other member functions -------------------------------------------------
248  // ===========================================================================
249 
251  auto src = GetSource(halfEdge).GetPosition();
252  auto trg = GetTarget(halfEdge).GetPosition();
253  auto dir = trg - src; // @todo normalize?
254  return std::atan2(static_cast<double>(dir.y), static_cast<double>(dir.x));
255  }
256 
257 #ifdef CARLA_ROAD_GENERATOR_EXTRA_LOG
258 
259  void DoublyConnectedEdgeList::PrintToLog() const
260  {
261  std::wstringstream sout;
262  {
263  sout << "iterate all nodes: ";
264  for (auto &node : GetNodes()) {
265  auto p = node.GetPosition();
266  sout << node << "{" << p.x << "," << p.y << "} ";
267  }
268  sout << "\n";
269  }
270  {
271  sout << "iterate all faces: ";
272  for (auto &face : GetFaces()) {
273  sout << face << " ";
274  }
275  sout << "\n";
276  }
277  {
278  sout << "iterate all edges: ";
279  for (auto &edge : GetHalfEdges()) {
280  auto &src = GetSource(edge);
281  auto &trg = GetTarget(edge);
282  auto &face = GetFace(edge);
283  sout << edge << "{" << src << "->" << trg << "," << face << "} ";
284  }
285  sout << "\n";
286  }
287  {
288  sout << "iterate nodes in face: ";
289  for (auto &face : GetFaces()) {
290  sout << face << "{ ";
291  auto &firstEdge = GetHalfEdge(face);
292  const auto *edge = &firstEdge;
293  do {
294  sout << GetSource(*edge) << " ";
295  edge = &GetNextInFace(*edge);
296  } while (edge != &firstEdge);
297  sout << "} ";
298  }
299  sout << "\n";
300  }
301  {
302  sout << "iterate edges in node: ";
303  for (auto &node : GetNodes()) {
304  sout << node << "{ ";
305  auto &firstEdge = GetLeavingHalfEdge(node);
306  const auto *edge = &firstEdge;
307  do {
308  sout << GetTarget(*edge) << " ";
309  edge = &GetNextInNode(*edge);
310  } while (edge != &firstEdge);
311  sout << "} ";
312  }
313  sout << "\n";
314  }
315  UE_LOG(LogCarla, Log, TEXT("\n%s"), sout.str().c_str());
316  }
317 
318 #endif // CARLA_ROAD_GENERATOR_EXTRA_LOG
319 
320 } // namespace MapGen
Node & SplitEdge(const Position &Position, HalfEdge &HalfEdge)
Split HalfEdge (and its pair) at Position.
static std::pair< DoublyConnectedEdgeList::HalfEdge *, DoublyConnectedEdgeList::HalfEdge * > FindPositionInNode(DoublyConnectedEdgeList::HalfEdge &halfEdge)
Return the pair {prev, next}, where prev/next is the previous/next edge counterclockwise around edge&#39;...
static Face & GetFace(HalfEdge &halfEdge)
static HalfEdge & GetHalfEdge(Face &face)
static float GetAngle(const HalfEdge &halfEdge)
Return the angle [-pi, pi] of the half-edge.
Simple doubly-connected edge list structure.
ListView< NodeIterator > GetNodes()
static Node & GetSource(HalfEdge &halfEdge)
ListView< HalfEdgeIterator > GetHalfEdges()
const DoublyConnectedEdgeList::Position & GetPosition() const
static HalfEdge & GetLeavingHalfEdge(Node &node)
static HalfEdge & GetNextInFace(HalfEdge &halfEdge)
DoublyConnectedEdgeList(const Position &Position0, const Position &Position1)
Create a DoublyConnectedEdgeList with two nodes, two edges and one face.
static Node & GetTarget(HalfEdge &halfEdge)
static HalfEdge & GetNextInNode(HalfEdge &halfEdge)
Node & AddNode(const Position &NodePosition, Node &OtherNode)
{
ListView< FaceIterator > GetFaces()
Face & ConnectNodes(Node &Node0, Node &Node1)
Connect two nodes by a pair of edges.