MyGAL
Diagram.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 <vector>
22 #include <list>
23 #include <unordered_set>
24 // My includes
25 #include "Box.h"
26 #include "Triangulation.h"
27 
31 namespace mygal
32 {
33 
34 template<typename T>
35 class FortuneAlgorithm;
36 
42 template<typename T>
43 class Diagram
44 {
45 public:
46  struct HalfEdge;
47  struct Face;
48 
52  struct Site
53  {
54  std::size_t index;
57  };
58 
62  struct Vertex
63  {
66  private:
67  friend Diagram<T>;
68  typename std::list<Vertex>::iterator it;
69  };
70 
74  struct HalfEdge
75  {
76  Vertex* origin = nullptr;
77  Vertex* destination = nullptr;
78  HalfEdge* twin = nullptr;
80  HalfEdge* prev = nullptr;
81  HalfEdge* next = nullptr;
83  private:
84  friend Diagram;
85  typename std::list<HalfEdge>::iterator it;
86  };
87 
94  struct Face
95  {
98  };
99 
100  // Remove copy operations
101 
102  Diagram(const Diagram&) = delete;
103  Diagram& operator=(const Diagram&) = delete;
104 
105  // Move operations
106 
110  Diagram(Diagram&&) = default;
111 
115  Diagram& operator=(Diagram&&) = default;
116 
117  // Accessors
118 
124  const std::vector<Site>& getSites() const
125  {
126  return mSites;
127  }
128 
136  const Site* getSite(std::size_t i) const
137  {
138  return &mSites[i];
139  }
140 
146  std::size_t getNbSites() const
147  {
148  return mSites.size();
149  }
150 
156  const std::vector<Face>& getFaces() const
157  {
158  return mFaces;
159  }
160 
168  const Face* getFace(std::size_t i) const
169  {
170  return &mFaces[i];
171  }
172 
178  const std::list<Vertex>& getVertices() const
179  {
180  return mVertices;
181  }
182 
188  const std::list<HalfEdge>& getHalfEdges() const
189  {
190  return mHalfEdges;
191  }
192 
193  // Intersection with a box
194 
202  bool intersect(Box<T> box)
203  {
204  auto success = true;
205  auto processedHalfEdges = std::unordered_set<HalfEdge*>();
206  auto verticesToRemove = std::unordered_set<Vertex*>();
207  for (const auto& site : mSites)
208  {
209  auto halfEdge = site.face->outerComponent;
210  auto inside = box.contains(halfEdge->origin->point);
211  auto outerComponentDirty = !inside;
212  auto incomingHalfEdge = static_cast<HalfEdge*>(nullptr); // First half edge coming in the box
213  auto outgoingHalfEdge = static_cast<HalfEdge*>(nullptr); // Last half edge going out the box
214  auto incomingSide = typename Box<T>::Side{};
215  auto outgoingSide = typename Box<T>::Side{};
216  do
217  {
218  auto intersections = std::array<typename Box<T>::Intersection, 2>{};
219  auto nbIntersections = box.getIntersections(halfEdge->origin->point, halfEdge->destination->point, intersections);
220  auto nextInside = box.contains(halfEdge->destination->point);
221  auto nextHalfEdge = halfEdge->next;
222  // The two points are outside the box
223  if (!inside && !nextInside)
224  {
225  // The edge is outside the box
226  if (nbIntersections == 0)
227  {
228  verticesToRemove.emplace(halfEdge->origin);
229  removeHalfEdge(halfEdge);
230  }
231  // The edge crosses twice the frontiers of the box
232  else if (nbIntersections == 2)
233  {
234  verticesToRemove.emplace(halfEdge->origin);
235  if (processedHalfEdges.find(halfEdge->twin) != processedHalfEdges.end())
236  {
237  halfEdge->origin = halfEdge->twin->destination;
238  halfEdge->destination = halfEdge->twin->origin;
239  }
240  else
241  {
242  halfEdge->origin = createVertex(intersections[0].point);
243  halfEdge->destination = createVertex(intersections[1].point);
244  }
245  if (outgoingHalfEdge != nullptr)
246  link(box, outgoingHalfEdge, outgoingSide, halfEdge, intersections[0].side);
247  if (incomingHalfEdge == nullptr)
248  {
249  incomingHalfEdge = halfEdge;
250  incomingSide = intersections[0].side;
251  }
252  outgoingHalfEdge = halfEdge;
253  outgoingSide = intersections[1].side;
254  processedHalfEdges.emplace(halfEdge);
255  }
256  else
257  success = false;
258  }
259  // The edge is going outside the box
260  else if (inside && !nextInside)
261  {
262  // We accept >= 1 as a corner can be found twice
263  if (nbIntersections >= 1)
264  {
265  if (processedHalfEdges.find(halfEdge->twin) != processedHalfEdges.end())
266  halfEdge->destination = halfEdge->twin->origin;
267  else
268  halfEdge->destination = createVertex(intersections[0].point);
269  outgoingHalfEdge = halfEdge;
270  outgoingSide = intersections[0].side;
271  processedHalfEdges.emplace(halfEdge);
272  }
273  else
274  success = false;
275  }
276  // The edge is coming inside the box
277  else if (!inside && nextInside)
278  {
279  // We accept >= 1 as a corner can be found twice
280  if (nbIntersections >= 1)
281  {
282  verticesToRemove.emplace(halfEdge->origin);
283  if (processedHalfEdges.find(halfEdge->twin) != processedHalfEdges.end())
284  halfEdge->origin = halfEdge->twin->destination;
285  else
286  halfEdge->origin = createVertex(intersections[0].point);
287  if (outgoingHalfEdge != nullptr)
288  link(box, outgoingHalfEdge, outgoingSide, halfEdge, intersections[0].side);
289  if (incomingHalfEdge == nullptr)
290  {
291  incomingHalfEdge = halfEdge;
292  incomingSide = intersections[0].side;
293  }
294  processedHalfEdges.emplace(halfEdge);
295  }
296  else
297  success = false;
298  }
299  halfEdge = nextHalfEdge;
300  // Update inside
301  inside = nextInside;
302  } while (halfEdge != site.face->outerComponent);
303  // Link the last and the first half edges inside the box
304  if (outerComponentDirty && incomingHalfEdge != nullptr)
305  link(box, outgoingHalfEdge, outgoingSide, incomingHalfEdge, incomingSide);
306  // Set outer component
307  if (outerComponentDirty)
308  site.face->outerComponent = incomingHalfEdge;
309  }
310  // Remove vertices
311  for (auto& vertex : verticesToRemove)
312  removeVertex(vertex);
313  // Return the status
314  return success;
315  }
316 
317  // Lloyd's relaxation
318 
328  std::vector<Vector2<T>> computeLloydRelaxation() const
329  {
330  auto sites = std::vector<Vector2<T>>();
331  for (const auto& face : mFaces)
332  {
333  auto area = static_cast<T>(0.0);
334  auto centroid = Vector2<T>();
335  auto halfEdge = face.outerComponent;
336  // Compute centroid of the face
337  do
338  {
339  auto det = halfEdge->origin->point.getDet(halfEdge->destination->point);
340  area += det;
341  centroid += (halfEdge->origin->point + halfEdge->destination->point) * det;
342  halfEdge = halfEdge->next;
343  } while (halfEdge != face.outerComponent);
344  area *= 0.5;
345  centroid *= 1.0 / (6.0 * area);
346  sites.push_back(centroid);
347  }
348  return sites;
349  }
350 
351  // Triangulation
352 
364  {
365  auto neighbors = std::vector<std::vector<std::size_t>>(mSites.size());
366  for (auto i = std::size_t(0); i < mSites.size(); ++i)
367  {
368  auto face = mFaces[i];
369  auto halfEdge = face.outerComponent;
370  while (halfEdge->prev != nullptr)
371  {
372  halfEdge = halfEdge->prev;
373  if (halfEdge == face.outerComponent)
374  break;
375  }
376  while (halfEdge != nullptr)
377  {
378  if (halfEdge->twin != nullptr)
379  neighbors[i].push_back(halfEdge->twin->incidentFace->site->index);
380  halfEdge = halfEdge->next;
381  if (halfEdge == face.outerComponent)
382  break;
383  }
384  }
385  return Triangulation(neighbors);
386  }
387 
388 private:
389  std::vector<Site> mSites;
390  std::vector<Face> mFaces;
391  std::list<Vertex> mVertices;
392  std::list<HalfEdge> mHalfEdges;
394  // Diagram construction
395 
396  template<typename>
397  friend class FortuneAlgorithm;
398 
399  Diagram(const std::vector<Vector2<T>>& points)
400  {
401  mSites.reserve(points.size());
402  mFaces.reserve(points.size());
403  for (auto i = std::size_t(0); i < points.size(); ++i)
404  {
405  mSites.push_back(Diagram::Site{i, points[i], nullptr});
406  mFaces.push_back(Diagram::Face{&mSites.back(), nullptr});
407  mSites.back().face = &mFaces.back();
408  }
409  }
410 
411  Site* getSite(std::size_t i)
412  {
413  return &mSites[i];
414  }
415 
416  Face* getFace(std::size_t i)
417  {
418  return &mFaces[i];
419  }
420 
421  Vertex* createVertex(Vector2<T> point)
422  {
423  mVertices.emplace_back();
424  mVertices.back().point = point;
425  mVertices.back().it = std::prev(mVertices.end());
426  return &mVertices.back();
427  }
428 
429  Vertex* createCorner(Box<T> box, typename Box<T>::Side side)
430  {
431  switch (side)
432  {
433  case Box<T>::Side::Left:
434  return createVertex(Vector2<T>(box.left, box.top));
436  return createVertex(Vector2<T>(box.left, box.bottom));
437  case Box<T>::Side::Right:
438  return createVertex(Vector2<T>(box.right, box.bottom));
439  case Box<T>::Side::Top:
440  return createVertex(Vector2<T>(box.right, box.top));
441  default:
442  return nullptr;
443  }
444  }
445 
446  HalfEdge* createHalfEdge(Face* face)
447  {
448  mHalfEdges.emplace_back();
449  mHalfEdges.back().incidentFace = face;
450  mHalfEdges.back().it = std::prev(mHalfEdges.end());
451  if (face->outerComponent == nullptr)
452  face->outerComponent = &mHalfEdges.back();
453  return &mHalfEdges.back();
454  }
455 
456  // Intersection with a box
457 
458  void link(Box<T> box, HalfEdge* start, typename Box<T>::Side startSide, HalfEdge* end, typename Box<T>::Side endSide)
459  {
460  auto halfEdge = start;
461  auto side = static_cast<int>(startSide);
462  while (side != static_cast<int>(endSide))
463  {
464  side = (side + 1) % 4;
465  halfEdge->next = createHalfEdge(start->incidentFace);
466  halfEdge->next->prev = halfEdge;
467  halfEdge->next->origin = halfEdge->destination;
468  halfEdge->next->destination = createCorner(box, static_cast<typename Box<T>::Side>(side));
469  halfEdge = halfEdge->next;
470  }
471  halfEdge->next = createHalfEdge(start->incidentFace);
472  halfEdge->next->prev = halfEdge;
473  end->prev = halfEdge->next;
474  halfEdge->next->next = end;
475  halfEdge->next->origin = halfEdge->destination;
476  halfEdge->next->destination = end->origin;
477  }
478 
479  void removeVertex(Vertex* vertex)
480  {
481  mVertices.erase(vertex->it);
482  }
483 
484  void removeHalfEdge(HalfEdge* halfEdge)
485  {
486  mHalfEdges.erase(halfEdge->it);
487  }
488 };
489 
490 }
Data structure representing a partitioning of the space.
Definition: Box.h:33
Vector2< T > point
Definition: Diagram.h:64
Class representing a box.
Definition: Box.h:47
const Site * getSite(std::size_t i) const
Get a site.
Definition: Diagram.h:136
Half-edge of a face.
Definition: Diagram.h:74
const std::vector< Site > & getSites() const
Get sites.
Definition: Diagram.h:124
const std::list< Vertex > & getVertices() const
Get vertices.
Definition: Diagram.h:178
Triangulation computeTriangulation() const
Compute the triangulation induced by the diagram.
Definition: Diagram.h:363
Implementation of Fortune&#39;s algorithm.
Definition: Box.h:36
std::vector< Vector2< T > > computeLloydRelaxation() const
Compute a Lloyd relaxation.
Definition: Diagram.h:328
Class representing a 2D vector.
Definition: Vector2.h:33
Namespace of MyGAL.
Definition: Box.h:29
Vertex of a face.
Definition: Diagram.h:62
const std::vector< Face > & getFaces() const
Get faces.
Definition: Diagram.h:156
HalfEdge * outerComponent
Definition: Diagram.h:97
std::size_t getNbSites() const
Get the number of sites.
Definition: Diagram.h:146
T bottom
Definition: Box.h:51
Data structure representing a triangulation.
Definition: Triangulation.h:34
T left
Definition: Box.h:50
T top
Definition: Box.h:53
bool intersect(Box< T > box)
Compute the intersection between the diagram and a box.
Definition: Diagram.h:202
Vector2< T > point
Definition: Diagram.h:55
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
std::size_t index
Definition: Diagram.h:54
Structure representing a cell in the diagram.
Definition: Diagram.h:94
const std::list< HalfEdge > & getHalfEdges() const
Get half-edges.
Definition: Diagram.h:188