intersect_polys_r2d.h
Go to the documentation of this file.
1 /*
2 This file is part of the Ristra portage project.
3 Please see the license file at the root of this repository, or at:
4  https://github.com/laristra/portage/blob/master/LICENSE
5 */
6 
7 #ifndef INTERSECT_POLYS_R2D_H
8 #define INTERSECT_POLYS_R2D_H
9 
10 #include <array>
11 #include <stdexcept>
12 #include <vector>
13 #include <algorithm>
14 
15 extern "C" {
16 #include "wonton/intersect/r3d/r2d.h"
17 }
18 
20 #include "wonton/support/Point.h"
21 
22 namespace Portage {
23 
24 // intersect one source polygon (possibly non-convex) with a
25 // triangular decomposition of a target polygon
26 
27 inline
28 std::vector<double>
29 intersect_polys_r2d(std::vector<Wonton::Point<2>> const & source_poly,
30  std::vector<Wonton::Point<2>> const & target_poly,
31  NumericTolerances_t num_tols) {
32 
33  std::vector<double> moments(3, 0);
34  bool src_convex = true;
35  bool trg_convex = true;
36 
37  const int POLY_ORDER = 1; // max degree of moments to calculate
38 
39  // Initialize source polygon
40 
41  const int size1 = source_poly.size();
42  const int size2 = target_poly.size();
43  if (!size1 || !size2)
44  return moments; // could allow top level code to avoid an 'if' statement
45 
46  std::vector<r2d_rvec2> verts1(size1);
47  for (int i = 0; i < size1; ++i) {
48  verts1[i].xy[0] = source_poly[i][0];
49  verts1[i].xy[1] = source_poly[i][1];
50  }
51 #ifdef DEBUG
52  double volume1 = 0.;
53  for (int i = 1; i < size1-1; ++i)
54  volume1 += r2d_orient(verts1[0], verts1[i], verts1[i+1]);
55  if (volume1 < 0.)
56  throw std::runtime_error("target_poly has negative volume");
57 #endif
58 
59  r2d_poly srcpoly_r2d;
60  r2d_init_poly(&srcpoly_r2d, &verts1[0], size1);
61 
62 #ifdef DEBUG
63  if (r2d_is_good(&srcpoly_r2d) == 0)
64  throw std::runtime_error("source_poly: invalid poly");
65 #endif
66 
67 
68  // Initialize target polygon and check for convexity
69 
70  std::vector<r2d_plane> faces(size2);
71  std::vector<r2d_rvec2> verts2(size2);
72  for (int i = 0; i < size2; ++i) {
73  verts2[i].xy[0] = target_poly[i][0];
74  verts2[i].xy[1] = target_poly[i][1];
75  }
76 
77  // check for convexity of target polygon
78  for (int i = 0; i < size2; ++i) {
79  //Compute distance from target_poly[(i+1)%size2]
80  //to segment (target_poly[i], target_poly[(i+2)%size2])
81  int ifv = i, imv = (i+1)%size2, isv = (i+2)%size2;
82  Wonton::Vector<2> normal( target_poly[isv][1] - target_poly[ifv][1],
83  -target_poly[isv][0] + target_poly[ifv][0]);
84  normal.normalize();
85  Wonton::Vector<2> fv2mv = target_poly[imv] - target_poly[ifv];
86  double dst = Wonton::dot(fv2mv, normal);
87 
88  if (dst <= -num_tols.min_absolute_distance) {
89  trg_convex = false;
90  break;
91  }
92  }
93  // case 1: target_poly is convex
94  // can simply use faces of target_poly as clip planes
95  if (trg_convex) {
96  r2d_poly_faces_from_verts(&faces[0], &verts2[0], size2);
97 
98  // clip the first poly against the faces of the second
99  r2d_clip(&srcpoly_r2d, &faces[0], size2);
100 
101  // find the moments (up to quadratic order) of the clipped poly
102  r2d_real om[R2D_NUM_MOMENTS(POLY_ORDER)];
103  r2d_reduce(&srcpoly_r2d, om, POLY_ORDER);
104 
105  // Check that the returned volume is positive (if the volume is zero,
106  // i.e. abs(om[0]) < eps, then it can sometimes be slightly negative,
107  // like om[0] == -1.24811e-16.
108  if (om[0] < num_tols.minimal_intersection_volume)
109  throw std::runtime_error("Negative volume");
110 
111  // Copy moments:
112  for (int j = 0; j < 3; ++j)
113  moments[j] = om[j];
114 
115  } else { // case 2: target_poly is non-convex
116 
117  // check for convexity of source polygon
118  for (int i = 0; i < size1; ++i) {
119  //Compute distance from source_poly[(i+1)%size1]
120  //to segment (source_poly[i], source_poly[(i+2)%size1])
121  int ifv = i, imv = (i+1)%size1, isv = (i+2)%size1;
122  Wonton::Vector<2> normal( source_poly[isv][1] - source_poly[ifv][1],
123  -source_poly[isv][0] + source_poly[ifv][0]);
124  normal.normalize();
125  Wonton::Vector<2> fv2mv = source_poly[imv] - source_poly[ifv];
126  double dst = Wonton::dot(fv2mv, normal);
127 
128  if (dst <= -num_tols.min_absolute_distance) {
129  src_convex = false;
130  break;
131  }
132  }
133  // if source polygon is convex while target polygon is non-convex,
134  // call the routine with the polygons reversed
135 
136  if (src_convex)
137  return intersect_polys_r2d(target_poly, source_poly, num_tols);
138  else {
139 
140  // Must divide target_poly into triangles for clipping. Choice
141  // of the central point is crucial. Try the centroid first -
142  // computed by the area weighted sum of centroids of any
143  // triangulation of the polygon
144  bool center_point_ok = true;
145  Wonton::Point<2> cen(0.0, 0.0);
146  r2d_rvec2 cenr2d;
147  cenr2d.xy[0] = 0.0; cenr2d.xy[1] = 0.0;
148  double area_sum = 0.0;
149  for (int i = 1; i < size2; ++i) {
150  double area = r2d_orient(verts2[0], verts2[i], verts2[(i+1)%size2]);
151  area_sum += area;
152  Wonton::Point<2> tricen =
153  (target_poly[0] + target_poly[i] + target_poly[(i+1)%size2])/3.0;
154  cen += area*tricen;
155  }
156  cen /= area_sum;
157  cenr2d.xy[0] = cen[0]; cenr2d.xy[1] = cen[1];
158 
159  for (int i = 0; i < size2; ++i)
160  if (r2d_orient(cenr2d, verts2[i], verts2[(i+1)%size2]) < 0.)
161  center_point_ok = false;
162 
163  if (!center_point_ok) {
164  // If the centroid is not ok, we have to find the center of
165  // the feasible set of the polygon. This means clipping the
166  // target_poly with its own face planes/lines. For a
167  // non-convex polygon, this will give a new polygon whose
168  // interior (See Garimella/Shashkov/Pavel paper on untangling)
169 
170  r2d_poly fspoly;
171  r2d_init_poly(&fspoly, &verts2[0], size2);
172 
173  r2d_poly_faces_from_verts(&faces[0], &verts2[0], size2);
174 
175  r2d_clip(&fspoly, &faces[0], size2);
176 
177  // If the resulting polygon is empty, we are out of luck
178  if (fspoly.nverts == 0)
179  std::runtime_error("Could not find a valid center point to triangulate non-convex polygon");
180 
181  // Have R2D compute first and second moments of polygon and
182  // get its centroid from that
183 
184  r2d_real fspoly_moments[R2D_NUM_MOMENTS(POLY_ORDER)];
185  r2d_reduce(&fspoly, fspoly_moments, POLY_ORDER);
186 
187  cen[0] = cenr2d.xy[0] = fspoly_moments[1]/fspoly_moments[0];
188  cen[1] = cenr2d.xy[1] = fspoly_moments[2]/fspoly_moments[0];
189 
190  // Even if the resulting feasible set polygon has vertices,
191  // maybe its degenerate. So we have to verify that its centroid
192  // indeed is a point that will give valid triangles when
193  // paired with the edges of the target polygon.
194 
195  for (int i = 0; i < size2; ++i)
196  if (r2d_orient(cenr2d, verts2[i], verts2[(i+1)%size2]) < 0.)
197  center_point_ok = false;
198  }
199 
200  // If we still don't have a good center point, we are out of luck
201  if (!center_point_ok)
202  std::runtime_error("Could not find a valid center point to triangulate non-convex polygon");
203 
204 
205  for (int i = 0; i < size2; ++i) {
206  verts2[0].xy[0] = cen[0];
207  verts2[0].xy[1] = cen[1];
208  verts2[1].xy[0] = target_poly[i][0];
209  verts2[1].xy[1] = target_poly[i][1];
210  verts2[2].xy[0] = target_poly[(i+1)%size2][0];
211  verts2[2].xy[1] = target_poly[(i+1)%size2][1];
212 
213  r2d_poly_faces_from_verts(&faces[0], &verts2[0], 3);
214 
215  r2d_poly srcpoly_r2d_copy = srcpoly_r2d;
216 
217  // clip the first poly against the faces of the second
218  r2d_clip(&srcpoly_r2d_copy, &faces[0], 3);
219 
220  // find the moments (up to quadratic order) of the clipped poly
221  // Only do this check in Debug mode:
222 #ifdef DEBUG
223  if (R2D_NUM_MOMENTS(POLY_ORDER) != 3)
224  throw std::runtime_error("Invalid number of moments");
225 #endif
226  r2d_real om[R2D_NUM_MOMENTS(POLY_ORDER)];
227  r2d_reduce(&srcpoly_r2d_copy, om, POLY_ORDER);
228 
229  // Check that the returned volume is positive (if the volume is zero,
230  // i.e. abs(om[0]) < eps, then it can sometimes be slightly negative,
231  // like om[0] == -1.24811e-16.
232  if (om[0] < num_tols.minimal_intersection_volume)
233  throw std::runtime_error("Negative volume for triangle of polygon");
234 
235  // Accumulate moments:
236  for (int j = 0; j < 3; ++j)
237  moments[j] += om[j];
238  } // for i
239  } // if (src_convex) ... else ...
240  } // if convex {} else {}
241 
242  return moments;
243 }
244 
245 } // namespace Portage
246 
247 #endif // INTERSECT_POLYS_R2D_H
double min_absolute_distance
Definition: portage.h:165
std::vector< T > vector
Definition: portage.h:238
Intersection and other tolerances to handle tiny values.
Definition: portage.h:152
double minimal_intersection_volume
Definition: portage.h:160
std::vector< double > intersect_polys_r2d(std::vector< Wonton::Point< 2 >> const &source_poly, std::vector< Wonton::Point< 2 >> const &target_poly, NumericTolerances_t num_tols)
Definition: intersect_polys_r2d.h:29
Definition: coredriver.h:42