Graphite  Version 3
An experimental 3D geometry processing program
generic_RVD_polygon.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2000-2022 Inria
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * * Neither the name of the ALICE Project-Team nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * Contact: Bruno Levy
30  *
31  * https://www.inria.fr/fr/bruno-levy
32  *
33  * Inria,
34  * Domaine de Voluceau,
35  * 78150 Le Chesnay - Rocquencourt
36  * FRANCE
37  *
38  */
39 
40 #ifndef GEOGRAM_VORONOI_GENERIC_RVD_POLYGON
41 #define GEOGRAM_VORONOI_GENERIC_RVD_POLYGON
42 
43 #include <geogram/basic/common.h>
46 
55 namespace GEOGen {
56 
64  class Polygon {
65  public:
66 
70  index_t nb_vertices() const {
71  return index_t(vertex_.size());
72  }
73 
80  const Vertex& vertex(index_t i) const {
82  return vertex_[i];
83  }
84 
93  return vertex_[i];
94  }
95 
104  return
105  (i == nb_vertices() - 1) ? 0 : (i + 1)
106  ;
107  }
108 
117  return (i == 0) ? (nb_vertices() - 1) : (i - 1);
118  }
119 
125  Vertex* add_vertex(const Vertex& v) {
126  vertex_.push_back(v);
127  return &*(vertex_.rbegin());
128  }
129 
133  void clear() {
134  vertex_.resize(0);
135  }
136 
141  void resize(index_t sz) {
142  vertex_.resize(sz);
143  }
144 
157  const Mesh* mesh, index_t f, bool symbolic,
158  const GEO::Attribute<double>& vertex_weight
159  );
160 
180  template <index_t DIM>
182  Polygon& target, PointAllocator& target_intersections,
183  const Mesh* mesh, const Delaunay* delaunay,
184  index_t i, index_t j,
185  bool exact, bool symbolic
186  ) {
187  if(exact) {
188  clip_by_plane_exact<DIM>(
189  target, target_intersections, mesh, delaunay, i, j
190  );
191  } else {
192  clip_by_plane_fast<DIM>(
193  target, target_intersections, delaunay, i, j, symbolic
194  );
195  }
196  }
197 
203  void copy(const Polygon& rhs) {
204  vertex_ = rhs.vertex_;
205  }
206 
212  void swap(Polygon& rhs) {
213  vertex_.swap(rhs.vertex_);
214  }
215 
216  protected:
241  template <index_t DIM>
243  Polygon& target, PointAllocator& target_intersections,
244  const Delaunay* delaunay, index_t i, index_t j,
245  bool symbolic
246  ) const {
247  target.clear();
248  if(nb_vertices() == 0) {
249  return;
250  }
251 
252  const double* geo_restrict pi = delaunay->vertex_ptr(i);
253  geo_assume_aligned(pi, geo_dim_alignment(DIM));
254  const double* geo_restrict pj = delaunay->vertex_ptr(j);
255  geo_assume_aligned(pj, geo_dim_alignment(DIM));
256 
257  // Compute d = n . m, where n is the
258  // normal vector of the bisector [pi,pj]
259  // and m the middle point of the bisector.
260  geo_decl_aligned(double d);
261  d = 0;
262  for(coord_index_t c = 0; c < DIM; ++c) {
263  d += (pi[c] + pj[c]) * (pi[c] - pj[c]);
264  }
265 
266  // The predecessor of the first vertex is the last vertex
267  index_t prev_k = nb_vertices() - 1;
268  const Vertex* prev_vk = &(vertex(prev_k));
269  const double* geo_restrict prev_pk = prev_vk->point();
270  geo_assume_aligned(prev_pk, geo_dim_alignment(DIM));
271 
272  // We compute:
273  // prev_l = prev_vk . n
274  geo_decl_aligned(double prev_l);
275  prev_l = 0.0;
276  for(coord_index_t c = 0; c < DIM; ++c) {
277  prev_l += prev_pk[c] * (pi[c] - pj[c]);
278  }
279 
280  // We compute:
281  // side1(pi,pj,q) = sign(2*q.n - n.m) = sign(2*l - d)
282  GEO::Sign prev_status = GEO::geo_sgn(2.0 * prev_l - d);
283 
284  for(index_t k = 0; k < nb_vertices(); k++) {
285  const Vertex* vk = &(vertex(k));
286  const double* pk = vk->point();
287 
288  // We compute: l = vk . n
289  geo_decl_aligned(double l);
290  l = 0.0;
291  for(coord_index_t c = 0; c < DIM; ++c) {
292  l += pk[c] * (pi[c] - pj[c]);
293  }
294 
295  // We compute:
296  // side1(pi,pj,q) = sign(2*q.n - n.m) = sign(2*l - d)
297  GEO::Sign status = GEO::geo_sgn(2.0 * l - d);
298 
299  // If status of edge extremities differ,
300  // then there is an intersection.
301  if(status != prev_status && (prev_status != 0)) {
302  Vertex I;
303  double* Ipoint = target_intersections.new_item();
304  I.set_point(Ipoint);
305  if(symbolic) {
306  if(
307  !I.sym().intersect_symbolic(
308  prev_vk->sym(), vk->sym(), j
309  )
310  ) {
311  // We encountered a problem. As a workaround,
312  // we copy prev_vk into the result.
313  I = *prev_vk;
314  }
315  }
316 
317  // Compute lambda1 and lambda2, the
318  // barycentric coordinates of the intersection I
319  // in the segment [prev_vk vk]
320  // Note that d and l (used for the predicates)
321  // are reused here.
322  double denom = 2.0 * (prev_l - l);
323  double lambda1, lambda2;
324 
325  // Shit happens ! [Forrest Gump]
326  if(::fabs(denom) < 1e-20) {
327  lambda1 = 0.5;
328  lambda2 = 0.5;
329  } else {
330  lambda1 = (d - 2.0 * l) / denom;
331  // Note: lambda2 is also given
332  // by (2.0*l2-d)/denom
333  // (but 1.0 - lambda1 is a bit
334  // faster to compute...)
335  lambda2 = 1.0 - lambda1;
336  }
337  // Compute intersection I by weighting
338  // the edge extremities with the barycentric
339  // coordinates lambda1 and lambda2
340  for(coord_index_t c = 0; c < DIM; ++c) {
341  Ipoint[c] =
342  lambda1 * prev_pk[c] +
343  lambda2 * pk[c];
344  }
345  I.set_weight(
346  lambda1 * prev_vk->weight() + lambda2 * vk->weight()
347  );
348  if(status > 0) {
349  I.copy_edge_from(*prev_vk);
351  } else {
352  I.set_flag(INTERSECT);
354  }
355  target.add_vertex(I);
356  }
357  if(status > 0) {
358  target.add_vertex(*vk);
359  }
360  prev_vk = vk;
361  prev_pk = pk;
362  prev_status = status;
363  prev_k = k;
364  prev_l = l;
365  }
366  }
367 
384  template <index_t DIM>
386  Polygon& target, PointAllocator& target_intersections,
387  const Mesh* mesh, const Delaunay* delaunay,
388  index_t i, index_t j
389  ) {
390  target.clear();
391  if(nb_vertices() == 0) {
392  return;
393  }
394 
395  const double* pi = delaunay->vertex_ptr(i);
396  const double* pj = delaunay->vertex_ptr(j);
397 
398  // The predecessor of the first vertex is the last vertex
399  index_t prev_k = nb_vertices() - 1;
400  const Vertex* prev_vk = &(vertex(prev_k));
401  Sign prev_status = side_exact(
402  mesh, delaunay, *prev_vk, pi, pj, DIM
403  );
404 
405  for(index_t k = 0; k < nb_vertices(); ++k) {
406  const Vertex* vk = &(vertex(k));
407  Sign status = side_exact(mesh, delaunay, *vk, pi, pj, DIM);
408 
409  // If status of edge extremities differ,
410  // there is an intersection.
411  if(status != prev_status && (prev_status != 0)) {
412 
413  Vertex I;
414  if(
415  !I.sym().intersect_symbolic(
416  prev_vk->sym(), vk->sym(), j
417  )
418  ) {
419  // We encountered a problem. As a workaround,
420  // we copy prev_vk into the result.
421  I = *prev_vk;
422  // geo_assert_not_reached ;
423  // not supposed to happen in exact mode
424  }
425  I.intersect_geom<DIM>(
426  target_intersections, *prev_vk, *vk, pi, pj
427  );
428  if(status > 0) {
429  I.copy_edge_from(*prev_vk);
431  } else {
432  I.set_flag(INTERSECT);
433  I.set_adjacent_seed(vk->adjacent_seed());
434  }
435  target.add_vertex(I);
436  }
437  if(status > 0) {
438  target.add_vertex(*vk);
439  }
440  prev_vk = vk;
441  prev_status = status;
442  prev_k = k;
443  }
444  }
445 
463  static Sign side_exact(
464  const Mesh* mesh, const Delaunay* delaunay,
465  const Vertex& q, const double* pi, const double* pj,
466  coord_index_t dim
467  );
468 
469  private:
470  GEO::vector<Vertex> vertex_;
471  };
472 }
473 
474 #endif
#define geo_debug_assert(x)
Verifies that a condition is met.
Definition: assert.h:196
Generic mechanism for attributes.
An allocator for points that are created from intersections in GenericVoronoiDiagram.
double * new_item()
Allocates a new point.
Internal representation of polygons for GenericVoronoiDiagram.
void initialize_from_mesh_facet(const Mesh *mesh, index_t f, bool symbolic, const GEO::Attribute< double > &vertex_weight)
Assigns a mesh facet to this Polygon.
index_t nb_vertices() const
Gets the number of vertices.
void clip_by_plane_exact(Polygon &target, PointAllocator &target_intersections, const Mesh *mesh, const Delaunay *delaunay, index_t i, index_t j)
Clips a Polygon with a plane (exact version).
Vertex & vertex(index_t i)
Gets a vertex by index.
index_t next_vertex(index_t i) const
Gets the index of the successor of a Vertex.
void copy(const Polygon &rhs)
Overwrites this Polygon with the contents of another polygon.
const Vertex & vertex(index_t i) const
Gets a vertex by index.
static Sign side_exact(const Mesh *mesh, const Delaunay *delaunay, const Vertex &q, const double *pi, const double *pj, coord_index_t dim)
Returns the position of a point relative to a bisector (exact version).
index_t prev_vertex(index_t i) const
Gets the index of the predecessor of a Vertex.
void resize(index_t sz)
Resizes this Polygon.
void clip_by_plane_fast(Polygon &target, PointAllocator &target_intersections, const Delaunay *delaunay, index_t i, index_t j, bool symbolic) const
Clips a Polygon with a plane (fast inexact version).
void clip_by_plane(Polygon &target, PointAllocator &target_intersections, const Mesh *mesh, const Delaunay *delaunay, index_t i, index_t j, bool exact, bool symbolic)
Clips a polygon with a plane.
void clear()
Clears this Polygon.
Vertex * add_vertex(const Vertex &v)
Adds a Vertex to this Polygon.
void swap(Polygon &rhs)
Swaps the contents of this Polygon and another polygon.
bool intersect_symbolic(const thisclass &v1, const thisclass &v2, index_t E)
Computes the symbolic representation of the intersection between a segment and a bisector.
Internal representation of vertices in GenericVoronoiDiagram.
void set_adjacent_seed(signed_index_t s)
Sets the adjacent seed.
void set_flag(EdgeFlag f)
Sets an EdgeFlag in this Vertex.
void intersect_geom(PointAllocator &target_intersections, const Vertex &vq1, const Vertex &vq2, const double *p1, const double *p2)
Computes the intersection between a segment and a bisector.
const SymbolicVertex & sym() const
Gets the symbolic representation.
void set_point(const double *p)
Sets the geometric location at this vertex.
double weight() const
Gets Vertex weight.
void copy_edge_from(const Vertex &rhs)
Copies adjacent facet and edge flags from another Vertex.
void set_weight(double w)
Sets the vertex weight.
signed_index_t adjacent_seed() const
Gets the adjacent seed.
const double * point() const
Gets the geometric location at this Vertex.
Abstract interface for Delaunay triangulation in Nd.
Definition: delaunay.h:71
const double * vertex_ptr(index_t i) const
Gets a pointer to a vertex by its global index.
Definition: delaunay.h:233
Represents a mesh.
Definition: mesh.h:2693
Vector with aligned memory allocation.
Definition: memory.h:635
Types and utilities for manipulating vertices in geometric and symbolic forms in restricted Voronoi d...
Common include file, providing basic definitions. Should be included before anything else by all head...
#define geo_dim_alignment(dim)
Gets a point alignment.
Definition: memory.h:270
geo_signed_index_t signed_index_t
The type for storing and manipulating indices differences.
Definition: numeric.h:343
Sign geo_sgn(const T &x)
Gets the sign of a value.
Definition: numeric.h:90
geo_index_t index_t
The type for storing and manipulating indices.
Definition: numeric.h:329
Sign
Integer constants that represent the sign of a value.
Definition: numeric.h:68
geo_coord_index_t coord_index_t
The type for storing coordinate indices, and iterating on the coordinates of a point.
Definition: numeric.h:363