MyGAL
FortuneAlgorithm.h
1 /* MyGAL
2  * Copyright (C) 2019 Pierre Vigier
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #pragma once
19 
20 // STL
21 #include <unordered_map>
22 // My includes
23 #include "PriorityQueue.h"
24 #include "Diagram.h"
25 #include "Beachline.h"
26 #include "Event.h"
27 #include "util.h"
28 
32 namespace mygal
33 {
34 
42 template<typename T>
43 class FortuneAlgorithm
44 {
45 public:
53  explicit FortuneAlgorithm(std::vector<Vector2<T>> points) : mDiagram(std::move(points))
54  {
55 
56  }
57 
64  void construct()
65  {
66  // Initialize event queue
67  for (auto i = std::size_t(0); i < mDiagram.getNbSites(); ++i)
68  mEvents.push(std::make_unique<Event<T>>(mDiagram.getSite(i)));
69 
70  // Process events
71  while (!mEvents.isEmpty())
72  {
73  auto event = mEvents.pop();
74  mBeachlineY = event->y;
75  if (event->type == Event<T>::Type::Site)
76  handleSiteEvent(event.get());
77  else
78  handleCircleEvent(event.get());
79  }
80  }
81 
95  bool bound(Box<T> box)
96  {
97  auto success = true;
98  // 1. Make sure the bounding box contains all the vertices
99  for (const auto& vertex : mDiagram.getVertices()) // Much faster when using vector<unique_ptr<Vertex*>, maybe we can only test vertices in border cells to speed up
100  {
101  box.left = std::min(vertex.point.x, box.left);
102  box.bottom = std::min(vertex.point.y, box.bottom);
103  box.right = std::max(vertex.point.x, box.right);
104  box.top = std::max(vertex.point.y, box.top);
105  }
106  // 2. Retrieve all non bounded half edges from the beach line
107  auto linkedVertices = std::list<LinkedVertex>();
108  auto vertices = VerticeOnFrontierContainer(mDiagram.getNbSites());
109  if (!mBeachline.isEmpty())
110  {
111  auto arc = mBeachline.getLeftmostArc();
112  while (!mBeachline.isNil(arc->next))
113  {
114  success = boundEdge(box, arc, arc->next, linkedVertices, vertices) && success;
115  arc = arc->next;
116  }
117  }
118  // 3. Add corners if necessary
119  for (auto& kv : vertices)
120  success = addCorners(box, linkedVertices, kv.second) && success;
121  // 4. Join the half-edges
122  for (auto& kv : vertices)
123  joinHalfEdges(kv.first, kv.second);
124  // Return the status
125  return success;
126  }
127 
136  {
137  return std::move(mDiagram);
138  }
139 
140 private:
141  Diagram<T> mDiagram;
142  Beachline<T> mBeachline;
143  PriorityQueue<Event<T>> mEvents;
144  T mBeachlineY;
145 
146  // Algorithm
147 
148  void handleSiteEvent(Event<T>* event)
149  {
150  auto site = event->site;
151  // 1. Check if the beachline is empty
152  if (mBeachline.isEmpty())
153  {
154  mBeachline.setRoot(mBeachline.createArc(site));
155  return;
156  }
157  // 2. Look for the arc above the site
158  auto arcToBreak = mBeachline.locateArcAbove(site->point, mBeachlineY);
159  deleteEvent(arcToBreak);
160  // 3. Replace this arc by the new arcs
161  auto middleArc = breakArc(arcToBreak, site);
162  auto leftArc = middleArc->prev;
163  auto rightArc = middleArc->next;
164  // 4. Add an edge in the diagram
165  addEdge(leftArc, middleArc);
166  middleArc->rightHalfEdge = middleArc->leftHalfEdge;
167  rightArc->leftHalfEdge = leftArc->rightHalfEdge;
168  // 5. Check circle events
169  // Left triplet
170  if (!mBeachline.isNil(leftArc->prev))
171  addEvent(leftArc->prev, leftArc, middleArc);
172  // Right triplet
173  if (!mBeachline.isNil(rightArc->next))
174  addEvent(middleArc, rightArc, rightArc->next);
175  }
176 
177  void handleCircleEvent(Event<T>* event)
178  {
179  auto point = event->point;
180  auto arc = event->arc;
181  // 1. Add vertex
182  auto vertex = mDiagram.createVertex(point);
183  // 2. Delete all the events with this arc
184  auto leftArc = arc->prev;
185  auto rightArc = arc->next;
186  deleteEvent(leftArc);
187  deleteEvent(rightArc);
188  // 3. Update the beachline and the diagram
189  removeArc(arc, vertex);
190  // 4. Add new circle events
191  // Left triplet
192  if (!mBeachline.isNil(leftArc->prev))
193  addEvent(leftArc->prev, leftArc, rightArc);
194  // Right triplet
195  if (!mBeachline.isNil(rightArc->next))
196  addEvent(leftArc, rightArc, rightArc->next);
197  }
198 
199  // Arcs
200 
201  Arc<T>* breakArc(Arc<T>* arc, typename Diagram<T>::Site* site)
202  {
203  // Create the new subtree
204  auto middleArc = mBeachline.createArc(site);
205  auto leftArc = mBeachline.createArc(arc->site, Arc<T>::Side::Left);
206  leftArc->leftHalfEdge = arc->leftHalfEdge;
207  auto rightArc = mBeachline.createArc(arc->site, Arc<T>::Side::Right);
208  rightArc->rightHalfEdge = arc->rightHalfEdge;
209  // Insert the subtree in the beachline
210  mBeachline.replace(arc, middleArc);
211  mBeachline.insertBefore(middleArc, leftArc);
212  mBeachline.insertAfter(middleArc, rightArc);
213  // Delete old arc
214  delete arc;
215  // Return the middle arc
216  return middleArc;
217  }
218 
219  void removeArc(Arc<T>* arc, typename Diagram<T>::Vertex* vertex)
220  {
221  // End edges
222  setDestination(arc->prev, arc, vertex);
223  setDestination(arc, arc->next, vertex);
224  // Join the edges of the middle arc
225  arc->leftHalfEdge->next = arc->rightHalfEdge;
226  arc->rightHalfEdge->prev = arc->leftHalfEdge;
227  // Update beachline
228  mBeachline.remove(arc);
229  // Create a new edge
230  auto prevHalfEdge = arc->prev->rightHalfEdge;
231  auto nextHalfEdge = arc->next->leftHalfEdge;
232  addEdge(arc->prev, arc->next);
233  setOrigin(arc->prev, arc->next, vertex);
234  setPrevHalfEdge(arc->prev->rightHalfEdge, prevHalfEdge);
235  setPrevHalfEdge(nextHalfEdge, arc->next->leftHalfEdge);
236  // Delete node
237  delete arc;
238  }
239 
240  // Breakpoints
241 
242  bool isMovingRight(const Arc<T>* left, const Arc<T>* right) const
243  {
244  return left->site->point.y < right->site->point.y;
245  }
246 
247  T getInitialX(const Arc<T>* left, const Arc<T>* right, bool movingRight) const
248  {
249  return movingRight ? left->site->point.x : right->site->point.x;
250  }
251 
252  // Edges
253 
254  void addEdge(Arc<T>* left, Arc<T>* right)
255  {
256  // Create two new half edges
257  left->rightHalfEdge = mDiagram.createHalfEdge(left->site->face);
258  right->leftHalfEdge = mDiagram.createHalfEdge(right->site->face);
259  // Set the two half edges twins
260  left->rightHalfEdge->twin = right->leftHalfEdge;
261  right->leftHalfEdge->twin = left->rightHalfEdge;
262  }
263 
264  void setOrigin(Arc<T>* left, Arc<T>* right, typename Diagram<T>::Vertex* vertex)
265  {
266  left->rightHalfEdge->destination = vertex;
267  right->leftHalfEdge->origin = vertex;
268  }
269 
270  void setDestination(Arc<T>* left, Arc<T>* right, typename Diagram<T>::Vertex* vertex)
271 {
272  left->rightHalfEdge->origin = vertex;
273  right->leftHalfEdge->destination = vertex;
274 }
275 
276  void setPrevHalfEdge(typename Diagram<T>::HalfEdge* prev, typename Diagram<T>::HalfEdge* next)
277  {
278  prev->next = next;
279  next->prev = prev;
280  }
281 
282  // Events
283 
284  void addEvent(Arc<T>* left, Arc<T>* middle, Arc<T>* right)
285  {
286  auto y = T();
287  auto convergencePoint = computeConvergencePoint(left->site->point, middle->site->point, right->site->point, y);
288  // Check that the bottom of the circle is below the beachline
289  if (!almostLower(y, mBeachlineY))
290  return;
291  // Check that the event is valid by checking that the middle arc will indeed shrink
292  // To do that we check in which direction the breakpoint are moving and that the
293  // convergence point is somewhere the breakpoints are moving to
294  auto leftBreakpointMovingRight = isMovingRight(left, middle);
295  auto rightBreakpointMovingRight = isMovingRight(middle, right);
296  auto leftInitialX = getInitialX(left, middle, leftBreakpointMovingRight);
297  auto rightInitialX = getInitialX(middle, right, rightBreakpointMovingRight);
298  if (!(!leftBreakpointMovingRight && rightBreakpointMovingRight) &&
299  ((leftBreakpointMovingRight && almostLower(leftInitialX, convergencePoint.x)) ||
300  (!leftBreakpointMovingRight && almostGreater(leftInitialX, convergencePoint.x))) &&
301  ((rightBreakpointMovingRight && almostLower(rightInitialX, convergencePoint.x)) ||
302  (!rightBreakpointMovingRight && almostGreater(rightInitialX, convergencePoint.x))))
303  {
304  auto event = std::make_unique<Event<T>>(y, convergencePoint, middle);
305  middle->event = event.get();
306  mEvents.push(std::move(event));
307  }
308  }
309 
310  void deleteEvent(Arc<T>* arc)
311  {
312  if (arc->event != nullptr)
313  {
314  mEvents.remove(arc->event->index);
315  arc->event = nullptr;
316  }
317  }
318 
319  Vector2<T> computeConvergencePoint(const Vector2<T>& point1, const Vector2<T>& point2, const Vector2<T>& point3, T& y) const
320  {
321  auto v1 = (point1 - point2).getOrthogonal();
322  auto v2 = (point2 - point3).getOrthogonal();
323  auto delta = static_cast<T>(0.5) * (point3 - point1);
324  auto denom = v1.getDet(v2);
325  // If there is no solution (points are aligned)
326  if (almostZero(denom))
327  {
328  // Infinity means that the event will never be added to the event queue
329  y = std::numeric_limits<T>::infinity();
330  return Vector2<T>();
331  }
332  // Otherwise, there is a solution
333  auto t = delta.getDet(v2) / denom;
334  auto center = static_cast<T>(0.5) * (point1 + point2) + t * v1;
335  auto r = center.getDistance(point1);
336  y = center.y - r;
337  return center;
338  }
339 
340  // Bounding
341 
342  struct LinkedVertex
343  {
344  typename Diagram<T>::HalfEdge* prevHalfEdge;
345  typename Diagram<T>::Vertex* vertex;
346  typename Diagram<T>::HalfEdge* nextHalfEdge;
347  };
348 
349  using VerticeOnFrontierContainer = std::unordered_map<std::size_t, std::array<LinkedVertex*, 8>>;
350 
351  bool boundEdge(const Box<T>& box, Arc<T>* leftArc, Arc<T>* rightArc, std::list<LinkedVertex>& linkedVertices,
352  VerticeOnFrontierContainer& vertices)
353  {
354  auto success = true;
355  // Bound the edge
356  auto direction = (leftArc->site->point - rightArc->site->point).getOrthogonal();
357  auto origin = (leftArc->site->point + rightArc->site->point) * static_cast<T>(0.5);
358  // Line-box intersection
359  auto intersection = box.getFirstIntersection(origin, direction);
360  // Create a new vertex and ends the half edges
361  auto vertex = mDiagram.createVertex(intersection.point);
362  setDestination(leftArc, rightArc, vertex);
363  // Initialize pointers
364  if (vertices.find(leftArc->site->index) == vertices.end())
365  vertices[leftArc->site->index].fill(nullptr);
366  if (vertices.find(rightArc->site->index) == vertices.end())
367  vertices[rightArc->site->index].fill(nullptr);
368  // Check that the vertices are not already assigned
369  success = vertices[leftArc->site->index][2 * static_cast<int>(intersection.side) + 1] == nullptr && success;
370  success = vertices[rightArc->site->index][2 * static_cast<int>(intersection.side)] == nullptr && success;
371  // Store the vertices on the boundaries
372  linkedVertices.emplace_back(LinkedVertex{nullptr, vertex, leftArc->rightHalfEdge});
373  vertices[leftArc->site->index][2 * static_cast<int>(intersection.side) + 1] = &linkedVertices.back();
374  linkedVertices.emplace_back(LinkedVertex{rightArc->leftHalfEdge, vertex, nullptr});
375  vertices[rightArc->site->index][2 * static_cast<int>(intersection.side)] = &linkedVertices.back();
376  // Return the status
377  return success;
378  }
379 
380  bool addCorners(const Box<T>& box, std::list<LinkedVertex>& linkedVertices, std::array<LinkedVertex*, 8>& cellVertices)
381  {
382  auto success = true;
383  // We check twice the first side to be sure that all necessary corners are added
384  for (auto i = std::size_t(0); i < 5; ++i)
385  {
386  auto side = i % 4;
387  auto nextSide = (side + 1) % 4;
388  // Add first corner
389  if (cellVertices[2 * side] == nullptr && cellVertices[2 * side + 1] != nullptr)
390  {
391  auto prevSide = (side + 3) % 4;
392  auto corner = mDiagram.createCorner(box, static_cast<typename Box<T>::Side>(side));
393  linkedVertices.emplace_back(LinkedVertex{nullptr, corner, nullptr});
394  // Check that we are not erasing an already assigned vertex
395  success = cellVertices[2 * prevSide + 1] == nullptr && success;
396  // Store the vertex on the boundary
397  cellVertices[2 * prevSide + 1] = &linkedVertices.back();
398  cellVertices[2 * side] = &linkedVertices.back();
399  }
400  // Add second corner
401  else if (cellVertices[2 * side] != nullptr && cellVertices[2 * side + 1] == nullptr)
402  {
403  auto corner = mDiagram.createCorner(box, static_cast<typename Box<T>::Side>(nextSide));
404  linkedVertices.emplace_back(LinkedVertex{nullptr, corner, nullptr});
405  // Check that we are not erasing an already assigned vertex
406  success = cellVertices[2 * nextSide] == nullptr && success;
407  // Store the vertex on the boundary
408  cellVertices[2 * side + 1] = &linkedVertices.back();
409  cellVertices[2 * nextSide] = &linkedVertices.back();
410  }
411  }
412  // Return the status
413  return success;
414  }
415 
416  void joinHalfEdges(std::size_t i, std::array<LinkedVertex*, 8>& cellVertices)
417  {
418  for (auto side = std::size_t(0); side < 4; ++side)
419  {
420  // After addCorners have been executed either both cellVertices[2 * side]
421  // and cellVertices[2 * side + 1] are assigned or both are nullptr
422  // Maybe we can add an assertion to check that
423  if (cellVertices[2 * side] != nullptr)
424  {
425  // Link vertices
426  auto halfEdge = mDiagram.createHalfEdge(mDiagram.getFace(i));
427  halfEdge->origin = cellVertices[2 * side]->vertex;
428  halfEdge->destination = cellVertices[2 * side + 1]->vertex;
429  cellVertices[2 * side]->nextHalfEdge = halfEdge;
430  halfEdge->prev = cellVertices[2 * side]->prevHalfEdge;
431  if (cellVertices[2 * side]->prevHalfEdge != nullptr)
432  cellVertices[2 * side]->prevHalfEdge->next = halfEdge;
433  cellVertices[2 * side + 1]->prevHalfEdge = halfEdge;
434  halfEdge->next = cellVertices[2 * side + 1]->nextHalfEdge;
435  if (cellVertices[2 * side + 1]->nextHalfEdge != nullptr)
436  cellVertices[2 * side + 1]->nextHalfEdge->prev = halfEdge;
437  }
438  }
439  }
440 };
441 
442 }
Data structure representing a partitioning of the space.
Definition: Box.h:33
bool bound(Box< T > box)
Bound the Voronoi diagram.
Class representing a box.
Definition: Box.h:47
Half-edge of a face.
Definition: Diagram.h:74
Class representing a 2D vector.
Definition: Vector2.h:33
Namespace of MyGAL.
Definition: Box.h:29
FortuneAlgorithm(std::vector< Vector2< T >> points)
Constructor of FortuneAlgorithm.
Vertex of a face.
Definition: Diagram.h:62
T bottom
Definition: Box.h:51
T getDistance(const Vector2< T > &other) const
Compute the euclidean distance of this point to another point.
Definition: Vector2.h:150
T left
Definition: Box.h:50
T top
Definition: Box.h:53
void construct()
Execute Fortune&#39;s algorithm to construct the diagram.
T right
Definition: Box.h:52
T getDet(const Vector2< T > &other) const
Compute the determinant with another vector.
Definition: Vector2.h:162
const Face * getFace(std::size_t i) const
Get a face.
Definition: Diagram.h:168
Point associated with a face of the partitioning.
Definition: Diagram.h:52
Diagram< T > getDiagram()
Return the constructed diagram.