Blender  V2.93
BLI_delaunay_2d_test.cc
Go to the documentation of this file.
1 /* Apache License, Version 2.0 */
2 
3 #include "testing/testing.h"
4 
5 #include "MEM_guardedalloc.h"
6 
7 extern "C" {
8 #include "BLI_math.h"
9 #include "BLI_rand.h"
10 #include "PIL_time.h"
11 }
12 
13 #include <fstream>
14 #include <iostream>
15 #include <sstream>
16 #include <type_traits>
17 
18 #define DO_CPP_TESTS 1
19 #define DO_C_TESTS 1
20 #define DO_RANDOM_TESTS 0
21 
22 #include "BLI_array.hh"
23 #include "BLI_double2.hh"
24 #include "BLI_math_boolean.hh"
25 #include "BLI_math_mpq.hh"
26 #include "BLI_mpq2.hh"
27 #include "BLI_vector.hh"
28 
29 #include "BLI_delaunay_2d.h"
30 
31 namespace blender::meshintersect {
32 
33 /* The spec should have the form:
34  * #verts #edges #faces
35  * <float> <float> [#verts lines)
36  * <int> <int> [#edges lines]
37  * <int> <int> ... <int> [#faces lines]
38  */
39 template<typename T> CDT_input<T> fill_input_from_string(const char *spec)
40 {
41  std::istringstream ss(spec);
42  std::string line;
43  getline(ss, line);
44  std::istringstream hdrss(line);
45  int nverts, nedges, nfaces;
46  hdrss >> nverts >> nedges >> nfaces;
47  if (nverts == 0) {
48  return CDT_input<T>();
49  }
50  Array<vec2<T>> verts(nverts);
51  Array<std::pair<int, int>> edges(nedges);
52  Array<Vector<int>> faces(nfaces);
53  int i = 0;
54  while (i < nverts && getline(ss, line)) {
55  std::istringstream iss(line);
56  double dp0, dp1;
57  iss >> dp0 >> dp1;
58  T p0(dp0);
59  T p1(dp1);
60  verts[i] = vec2<T>(p0, p1);
61  i++;
62  }
63  i = 0;
64  while (i < nedges && getline(ss, line)) {
65  std::istringstream ess(line);
66  int e0, e1;
67  ess >> e0 >> e1;
68  edges[i] = std::pair<int, int>(e0, e1);
69  i++;
70  }
71  i = 0;
72  while (i < nfaces && getline(ss, line)) {
73  std::istringstream fss(line);
74  int v;
75  while (fss >> v) {
76  faces[i].append(v);
77  }
78  i++;
79  }
80  CDT_input<T> ans;
81  ans.vert = verts;
82  ans.edge = edges;
83  ans.face = faces;
84 #ifdef WITH_GMP
85  if (std::is_same<mpq_class, T>::value) {
86  ans.epsilon = T(0);
87  }
88  else {
89  ans.epsilon = T(0.00001);
90  }
91 #else
92  ans.epsilon = T(0.00001);
93 #endif
94  return ans;
95 }
96 
97 /* Find an original index in a table mapping new to original.
98  * Return -1 if not found.
99  */
100 static int get_orig_index(const Array<Vector<int>> &out_to_orig, int orig_index)
101 {
102  int n = static_cast<int>(out_to_orig.size());
103  for (int i = 0; i < n; ++i) {
104  for (int orig : out_to_orig[i]) {
105  if (orig == orig_index) {
106  return i;
107  }
108  }
109  }
110  return -1;
111 }
112 
113 template<typename T> static double math_to_double(const T UNUSED(v))
114 {
115  BLI_assert(false); /* Need implementation for other type. */
116  return 0.0;
117 }
118 
119 template<> double math_to_double<double>(const double v)
120 {
121  return v;
122 }
123 
124 #ifdef WITH_GMP
125 template<> double math_to_double<mpq_class>(const mpq_class v)
126 {
127  return v.get_d();
128 }
129 #endif
130 
131 template<typename T> static T math_abs(const T v);
132 
133 #ifdef WITH_GMP
134 template<> mpq_class math_abs(const mpq_class v)
135 {
136  return abs(v);
137 }
138 #endif
139 
140 template<> double math_abs(const double v)
141 {
142  return fabs(v);
143 }
144 
145 /* Find an output index corresponding to a given coordinate (approximately).
146  * Return -1 if not found.
147  */
148 template<typename T> int get_vertex_by_coord(const CDT_result<T> &out, double x, double y)
149 {
150  int nv = static_cast<int>(out.vert.size());
151  for (int i = 0; i < nv; ++i) {
152  double vx = math_to_double(out.vert[i][0]);
153  double vy = math_to_double(out.vert[i][1]);
154  if (fabs(vx - x) <= 1e-5 && fabs(vy - y) <= 1e-5) {
155  return i;
156  }
157  }
158  return -1;
159 }
160 
161 /* Find an edge between two given output vertex indices. -1 if not found, */
162 template<typename T>
163 int get_output_edge_index(const CDT_result<T> &out, int out_index_1, int out_index_2)
164 {
165  int ne = static_cast<int>(out.edge.size());
166  for (int i = 0; i < ne; ++i) {
167  if ((out.edge[i].first == out_index_1 && out.edge[i].second == out_index_2) ||
168  (out.edge[i].first == out_index_2 && out.edge[i].second == out_index_1)) {
169  return i;
170  }
171  }
172  return -1;
173 }
174 
175 template<typename T>
176 bool output_edge_has_input_id(const CDT_result<T> &out, int out_edge_index, int in_edge_index)
177 {
178  return out_edge_index < static_cast<int>(out.edge_orig.size()) &&
179  out.edge_orig[out_edge_index].contains(in_edge_index);
180 }
181 
182 /* Which out face is for a give output vertex ngon? -1 if not found.
183  * Allow for cyclic shifts vertices of one poly vs the other.
184  */
185 template<typename T> int get_output_face_index(const CDT_result<T> &out, const Array<int> &poly)
186 {
187  int nf = static_cast<int>(out.face.size());
188  int npolyv = static_cast<int>(poly.size());
189  for (int f = 0; f < nf; ++f) {
190  if (out.face[f].size() != poly.size()) {
191  continue;
192  }
193  for (int cycle_start = 0; cycle_start < npolyv; ++cycle_start) {
194  bool ok = true;
195  for (int k = 0; ok && k < npolyv; ++k) {
196  if (out.face[f][(cycle_start + k) % npolyv] != poly[k]) {
197  ok = false;
198  }
199  }
200  if (ok) {
201  return f;
202  }
203  }
204  }
205  return -1;
206 }
207 
208 template<typename T>
210  int out_index_1,
211  int out_index_2,
212  int out_index_3)
213 {
214  Array<int> tri{out_index_1, out_index_2, out_index_3};
215  return get_output_face_index(out, tri);
216 }
217 
218 template<typename T>
219 bool output_face_has_input_id(const CDT_result<T> &out, int out_face_index, int in_face_index)
220 {
221  return out_face_index < static_cast<int>(out.face_orig.size()) &&
222  out.face_orig[out_face_index].contains(in_face_index);
223 }
224 
225 /* For debugging. */
226 template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_result<T> &r)
227 {
228  os << "\nRESULT\n";
229  os << r.vert.size() << " verts, " << r.edge.size() << " edges, " << r.face.size() << " faces\n";
230  os << "\nVERTS\n";
231  for (int i : r.vert.index_range()) {
232  os << "v" << i << " = " << r.vert[i] << "\n";
233  os << " orig: ";
234  for (int j : r.vert_orig[i].index_range()) {
235  os << r.vert_orig[i][j] << " ";
236  }
237  os << "\n";
238  }
239  os << "\nEDGES\n";
240  for (int i : r.edge.index_range()) {
241  os << "e" << i << " = (" << r.edge[i].first << ", " << r.edge[i].second << ")\n";
242  os << " orig: ";
243  for (int j : r.edge_orig[i].size()) {
244  os << r.edge_orig[i][j] << " ";
245  }
246  os << "\n";
247  }
248  os << "\nFACES\n";
249  for (int i : r.face.index_range()) {
250  os << "f" << i << " = ";
251  for (int j : r.face[i].index_range()) {
252  os << r.face[i][j] << " ";
253  }
254  os << "\n";
255  os << " orig: ";
256  for (int j : r.face_orig[i].index_range()) {
257  os << r.face_orig[i][j] << " ";
258  }
259  os << "\n";
260  }
261  return os;
262 }
263 
264 static bool draw_append = false; /* Will be set to true after first call. */
265 
266 template<typename T>
267 void graph_draw(const std::string &label,
268  const Array<vec2<T>> &verts,
269  const Array<std::pair<int, int>> &edges,
270  const Array<Vector<int>> &UNUSED(faces))
271 {
272  /* Would like to use BKE_tempdir_base() here, but that brings in dependence on kernel library.
273  * This is just for developer debugging anyway, and should never be called in production Blender.
274  */
275 #ifdef WIN32
276  constexpr const char *drawfile = "./cdt_test_draw.html";
277 #else
278  constexpr const char *drawfile = "/tmp/cdt_test_draw.html";
279 #endif
280  constexpr int max_draw_width = 1400;
281  constexpr int max_draw_height = 1000;
282  constexpr int thin_line = 1;
283  constexpr int vert_radius = 3;
284  constexpr bool draw_vert_labels = true;
285  constexpr bool draw_edge_labels = false;
286 
287  if (verts.size() == 0) {
288  return;
289  }
290  vec2<double> vmin(1e10, 1e10);
291  vec2<double> vmax(-1e10, -1e10);
292  for (const vec2<T> &v : verts) {
293  for (int i = 0; i < 2; ++i) {
294  double dvi = math_to_double(v[i]);
295  if (dvi < vmin[i]) {
296  vmin[i] = dvi;
297  }
298  if (dvi > vmax[i]) {
299  vmax[i] = dvi;
300  }
301  }
302  }
303  double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * 0.05;
304  double minx = vmin.x - draw_margin;
305  double maxx = vmax.x + draw_margin;
306  double miny = vmin.y - draw_margin;
307  double maxy = vmax.y + draw_margin;
308 
309  double width = maxx - minx;
310  double height = maxy - miny;
311  double aspect = height / width;
312  int view_width = max_draw_width;
313  int view_height = static_cast<int>(view_width * aspect);
314  if (view_height > max_draw_height) {
315  view_height = max_draw_height;
316  view_width = static_cast<int>(view_height / aspect);
317  }
318  double scale = view_width / width;
319 
320 #define SX(x) ((math_to_double(x) - minx) * scale)
321 #define SY(y) ((maxy - math_to_double(y)) * scale)
322 
323  std::ofstream f;
324  if (draw_append) {
325  f.open(drawfile, std::ios_base::app);
326  }
327  else {
328  f.open(drawfile);
329  }
330  if (!f) {
331  std::cout << "Could not open file " << drawfile << "\n";
332  return;
333  }
334 
335  f << "<div>" << label << "</div>\n<div>\n"
336  << "<svg version=\"1.1\" "
337  "xmlns=\"http://www.w3.org/2000/svg\" "
338  "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
339  "xml:space=\"preserve\"\n"
340  << "width=\"" << view_width << "\" height=\"" << view_height << "\">n";
341 
342  for (const std::pair<int, int> &e : edges) {
343  const vec2<T> &uco = verts[e.first];
344  const vec2<T> &vco = verts[e.second];
345  int strokew = thin_line;
346  f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\""
347  << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\""
348  << SY(vco[1]) << "\">\n";
349  f << " <title>[" << e.first << "][" << e.second << "]</title>\n";
350  f << "</line>\n";
351  if (draw_edge_labels) {
352  f << "<text x=\"" << SX(0.5 * (uco[0] + vco[0])) << "\" y=\"" << SY(0.5 * (uco[1] + vco[1]))
353  << "\" font-size=\"small\">";
354  f << "[" << e.first << "][" << e.second << "]</text>\n";
355  }
356  }
357 
358  int i = 0;
359  for (const vec2<T> &vco : verts) {
360  f << "<circle fill=\"black\" cx=\"" << SX(vco[0]) << "\" cy=\"" << SY(vco[1]) << "\" r=\""
361  << vert_radius << "\">\n";
362  f << " <title>[" << i << "]" << vco << "</title>\n";
363  f << "</circle>\n";
364  if (draw_vert_labels) {
365  f << "<text x=\"" << SX(vco[0]) + vert_radius << "\" y=\"" << SY(vco[1]) - vert_radius
366  << "\" font-size=\"small\">[" << i << "]</text>\n";
367  }
368  ++i;
369  }
370 
371  draw_append = true;
372 #undef SX
373 #undef SY
374 }
375 
376 /* Should tests draw their output to an html file? */
377 constexpr bool DO_DRAW = false;
378 
379 template<typename T> void expect_coord_near(const vec2<T> &testco, const vec2<T> &refco);
380 
381 #ifdef WITH_GMP
382 template<>
383 void expect_coord_near<mpq_class>(const vec2<mpq_class> &testco, const vec2<mpq_class> &refco)
384 {
385  EXPECT_EQ(testco[0], refco[0]);
386  EXPECT_EQ(testco[0], refco[0]);
387 }
388 #endif
389 
390 template<> void expect_coord_near<double>(const vec2<double> &testco, const vec2<double> &refco)
391 {
392  EXPECT_NEAR(testco[0], refco[0], 1e-5);
393  EXPECT_NEAR(testco[1], refco[1], 1e-5);
394 }
395 
396 #if DO_CPP_TESTS
397 
398 template<typename T> void empty_test()
399 {
400  CDT_input<T> in;
401 
402  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
403  EXPECT_EQ(0, out.vert.size());
404  EXPECT_EQ(0, out.edge.size());
405  EXPECT_EQ(0, out.face.size());
406  EXPECT_EQ(0, out.vert_orig.size());
407  EXPECT_EQ(0, out.edge_orig.size());
408  EXPECT_EQ(0, out.face_orig.size());
409 }
410 
411 template<typename T> void onept_test()
412 {
413  const char *spec = R"(1 0 0
414  0.0 0.0
415  )";
416 
417  CDT_input<T> in = fill_input_from_string<T>(spec);
418  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
419  EXPECT_EQ(out.vert.size(), 1);
420  EXPECT_EQ(out.edge.size(), 0);
421  EXPECT_EQ(out.face.size(), 0);
422  if (out.vert.size() >= 1) {
423  expect_coord_near<T>(out.vert[0], vec2<T>(0, 0));
424  }
425 }
426 
427 template<typename T> void twopt_test()
428 {
429  const char *spec = R"(2 0 0
430  0.0 -0.75
431  0.0 0.75
432  )";
433 
434  CDT_input<T> in = fill_input_from_string<T>(spec);
435  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
436  EXPECT_EQ(out.vert.size(), 2);
437  EXPECT_EQ(out.edge.size(), 1);
438  EXPECT_EQ(out.face.size(), 0);
439  int v0_out = get_orig_index(out.vert_orig, 0);
440  int v1_out = get_orig_index(out.vert_orig, 1);
441  EXPECT_NE(v0_out, -1);
442  EXPECT_NE(v1_out, -1);
443  EXPECT_NE(v0_out, v1_out);
444  if (out.vert.size() >= 1) {
445  expect_coord_near<T>(out.vert[v0_out], vec2<T>(0.0, -0.75));
446  expect_coord_near<T>(out.vert[v1_out], vec2<T>(0.0, 0.75));
447  }
448  int e0_out = get_output_edge_index(out, v0_out, v1_out);
449  EXPECT_EQ(e0_out, 0);
450  if (DO_DRAW) {
451  graph_draw<T>("TwoPt", out.vert, out.edge, out.face);
452  }
453 }
454 
455 template<typename T> void threept_test()
456 {
457  const char *spec = R"(3 0 0
458  -0.1 -0.75
459  0.1 0.75
460  0.5 0.5
461  )";
462 
463  CDT_input<T> in = fill_input_from_string<T>(spec);
464  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
465  EXPECT_EQ(out.vert.size(), 3);
466  EXPECT_EQ(out.edge.size(), 3);
467  EXPECT_EQ(out.face.size(), 1);
468  int v0_out = get_orig_index(out.vert_orig, 0);
469  int v1_out = get_orig_index(out.vert_orig, 1);
470  int v2_out = get_orig_index(out.vert_orig, 2);
471  EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1);
472  EXPECT_TRUE(v0_out != v1_out && v0_out != v2_out && v1_out != v2_out);
473  int e0_out = get_output_edge_index(out, v0_out, v1_out);
474  int e1_out = get_output_edge_index(out, v1_out, v2_out);
475  int e2_out = get_output_edge_index(out, v2_out, v0_out);
476  EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1);
477  EXPECT_TRUE(e0_out != e1_out && e0_out != e2_out && e1_out != e2_out);
478  int f0_out = get_output_tri_index(out, v0_out, v2_out, v1_out);
479  EXPECT_EQ(f0_out, 0);
480  if (DO_DRAW) {
481  graph_draw<T>("ThreePt", out.vert, out.edge, out.face);
482  }
483 }
484 
485 template<typename T> void mixedpts_test()
486 {
487  /* Edges form a chain of length 3. */
488  const char *spec = R"(4 3 0
489  0.0 0.0
490  -0.5 -0.5
491  -0.4 -0.25
492  -0.3 0.8
493  0 1
494  1 2
495  2 3
496  )";
497 
498  CDT_input<T> in = fill_input_from_string<T>(spec);
499  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
500  EXPECT_EQ(out.vert.size(), 4);
501  EXPECT_EQ(out.edge.size(), 6);
502  int v0_out = get_orig_index(out.vert_orig, 0);
503  int v1_out = get_orig_index(out.vert_orig, 1);
504  int v2_out = get_orig_index(out.vert_orig, 2);
505  int v3_out = get_orig_index(out.vert_orig, 3);
506  EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1);
507  int e0_out = get_output_edge_index(out, v0_out, v1_out);
508  int e1_out = get_output_edge_index(out, v1_out, v2_out);
509  int e2_out = get_output_edge_index(out, v2_out, v3_out);
510  EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1);
511  EXPECT_TRUE(output_edge_has_input_id(out, e0_out, 0));
512  EXPECT_TRUE(output_edge_has_input_id(out, e1_out, 1));
513  EXPECT_TRUE(output_edge_has_input_id(out, e2_out, 2));
514  if (DO_DRAW) {
515  graph_draw<T>("MixedPts", out.vert, out.edge, out.face);
516  }
517 }
518 
519 template<typename T> void quad0_test()
520 {
521  const char *spec = R"(4 0 0
522  0.0 1.0
523  1.0 0.0
524  2.0 0.1
525  2.25 0.5
526  )";
527 
528  CDT_input<T> in = fill_input_from_string<T>(spec);
529  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
530  EXPECT_EQ(out.vert.size(), 4);
531  EXPECT_EQ(out.edge.size(), 5);
532  int e_diag_out = get_output_edge_index(out, 1, 3);
533  EXPECT_NE(e_diag_out, -1);
534  if (DO_DRAW) {
535  graph_draw<T>("Quad0", out.vert, out.edge, out.face);
536  }
537 }
538 
539 template<typename T> void quad1_test()
540 {
541  const char *spec = R"(4 0 0
542  0.0 0.0
543  0.9 -1.0
544  2.0 0.0
545  0.9 3.0
546  )";
547 
548  CDT_input<T> in = fill_input_from_string<T>(spec);
549  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
550  EXPECT_EQ(out.vert.size(), 4);
551  EXPECT_EQ(out.edge.size(), 5);
552  int e_diag_out = get_output_edge_index(out, 0, 2);
553  EXPECT_NE(e_diag_out, -1);
554  if (DO_DRAW) {
555  graph_draw<T>("Quad1", out.vert, out.edge, out.face);
556  }
557 }
558 
559 template<typename T> void quad2_test()
560 {
561  const char *spec = R"(4 0 0
562  0.5 0.0
563  0.15 0.2
564  0.3 0.4
565  .45 0.35
566  )";
567 
568  CDT_input<T> in = fill_input_from_string<T>(spec);
569  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
570  EXPECT_EQ(out.vert.size(), 4);
571  EXPECT_EQ(out.edge.size(), 5);
572  int e_diag_out = get_output_edge_index(out, 1, 3);
573  EXPECT_NE(e_diag_out, -1);
574  if (DO_DRAW) {
575  graph_draw<T>("Quad2", out.vert, out.edge, out.face);
576  }
577 }
578 
579 template<typename T> void quad3_test()
580 {
581  const char *spec = R"(4 0 0
582  0.5 0.0
583  0.0 0.0
584  0.3 0.4
585  .45 0.35
586  )";
587 
588  CDT_input<T> in = fill_input_from_string<T>(spec);
589  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
590  EXPECT_EQ(out.vert.size(), 4);
591  EXPECT_EQ(out.edge.size(), 5);
592  int e_diag_out = get_output_edge_index(out, 0, 2);
593  EXPECT_NE(e_diag_out, -1);
594  if (DO_DRAW) {
595  graph_draw<T>("Quad3", out.vert, out.edge, out.face);
596  }
597 }
598 
599 template<typename T> void quad4_test()
600 {
601  const char *spec = R"(4 0 0
602  1.0 1.0
603  0.0 0.0
604  1.0 -3.0
605  0.0 1.0
606  )";
607 
608  CDT_input<T> in = fill_input_from_string<T>(spec);
609  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
610  EXPECT_EQ(out.vert.size(), 4);
611  EXPECT_EQ(out.edge.size(), 5);
612  int e_diag_out = get_output_edge_index(out, 0, 1);
613  EXPECT_NE(e_diag_out, -1);
614  if (DO_DRAW) {
615  graph_draw<T>("Quad4", out.vert, out.edge, out.face);
616  }
617 }
618 
619 template<typename T> void lineinsquare_test()
620 {
621  const char *spec = R"(6 1 1
622  -0.5 -0.5
623  0.5 -0.5
624  -0.5 0.5
625  0.5 0.5
626  -0.25 0.0
627  0.25 0.0
628  4 5
629  0 1 3 2
630  )";
631 
632  CDT_input<T> in = fill_input_from_string<T>(spec);
633  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
634  EXPECT_EQ(out.vert.size(), 6);
635  EXPECT_EQ(out.face.size(), 6);
636  if (DO_DRAW) {
637  graph_draw<T>("LineInSquare - full", out.vert, out.edge, out.face);
638  }
639  CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS);
640  EXPECT_EQ(out2.vert.size(), 6);
641  EXPECT_EQ(out2.face.size(), 1);
642  if (DO_DRAW) {
643  graph_draw<T>("LineInSquare - constraints", out2.vert, out2.edge, out2.face);
644  }
645 }
646 
647 template<typename T> void crosssegs_test()
648 {
649  const char *spec = R"(4 2 0
650  -0.5 0.0
651  0.5 0.0
652  -0.4 -0.5
653  0.4 0.5
654  0 1
655  2 3
656  )";
657 
658  CDT_input<T> in = fill_input_from_string<T>(spec);
659  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
660  EXPECT_EQ(out.vert.size(), 5);
661  EXPECT_EQ(out.edge.size(), 8);
662  EXPECT_EQ(out.face.size(), 4);
663  int v0_out = get_orig_index(out.vert_orig, 0);
664  int v1_out = get_orig_index(out.vert_orig, 1);
665  int v2_out = get_orig_index(out.vert_orig, 2);
666  int v3_out = get_orig_index(out.vert_orig, 3);
667  EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1);
668  if (out.vert.size() == 5) {
669  int v_intersect = -1;
670  for (int i = 0; i < 5; i++) {
671  if (!ELEM(i, v0_out, v1_out, v2_out, v3_out)) {
672  EXPECT_EQ(v_intersect, -1);
673  v_intersect = i;
674  }
675  }
676  EXPECT_NE(v_intersect, -1);
677  if (v_intersect != -1) {
678  expect_coord_near<T>(out.vert[v_intersect], vec2<T>(0, 0));
679  }
680  }
681  if (DO_DRAW) {
682  graph_draw<T>("CrossSegs", out.vert, out.edge, out.face);
683  }
684 }
685 
686 template<typename T> void diamondcross_test()
687 {
688  /* Diamond with constraint edge from top to bottom. Some dup verts. */
689  const char *spec = R"(7 5 0
690  0.0 0.0
691  1.0 3.0
692  2.0 0.0
693  1.0 -3.0
694  0.0 0.0
695  1.0 -3.0
696  1.0 3.0
697  0 1
698  1 2
699  2 3
700  3 4
701  5 6
702  )";
703 
704  CDT_input<T> in = fill_input_from_string<T>(spec);
705  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
706  EXPECT_EQ(out.vert.size(), 4);
707  EXPECT_EQ(out.edge.size(), 5);
708  EXPECT_EQ(out.face.size(), 2);
709  if (DO_DRAW) {
710  graph_draw<T>("DiamondCross", out.vert, out.edge, out.face);
711  }
712 }
713 
714 template<typename T> void twodiamondscross_test()
715 {
716  const char *spec = R"(12 9 0
717  0.0 0.0
718  1.0 2.0
719  2.0 0.0
720  1.0 -2.0
721  0.0 0.0
722  3.0 0.0
723  4.0 2.0
724  5.0 0.0
725  4.0 -2.0
726  3.0 0.0
727  0.0 0.0
728  5.0 0.0
729  0 1
730  1 2
731  2 3
732  3 4
733  5 6
734  6 7
735  7 8
736  8 9
737  10 11
738  )";
739 
740  CDT_input<T> in = fill_input_from_string<T>(spec);
741  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
742  EXPECT_EQ(out.vert.size(), 8);
743  EXPECT_EQ(out.edge.size(), 15);
744  EXPECT_EQ(out.face.size(), 8);
745  if (out.vert.size() == 8 && out.edge.size() == 15 && out.face.size() == 8) {
746  int v_out[12];
747  for (int i = 0; i < 12; ++i) {
748  v_out[i] = get_orig_index(out.vert_orig, i);
749  EXPECT_NE(v_out[i], -1);
750  }
751  EXPECT_EQ(v_out[0], v_out[4]);
752  EXPECT_EQ(v_out[0], v_out[10]);
753  EXPECT_EQ(v_out[5], v_out[9]);
754  EXPECT_EQ(v_out[7], v_out[11]);
755  int e_out[9];
756  for (int i = 0; i < 8; ++i) {
757  e_out[i] = get_output_edge_index(out, v_out[in.edge[i].first], v_out[in.edge[i].second]);
758  EXPECT_NE(e_out[i], -1);
759  }
760  /* there won't be a single edge for the input cross edge, but rather 3 */
761  EXPECT_EQ(get_output_edge_index(out, v_out[10], v_out[11]), -1);
762  int e_cross_1 = get_output_edge_index(out, v_out[0], v_out[2]);
763  int e_cross_2 = get_output_edge_index(out, v_out[2], v_out[5]);
764  int e_cross_3 = get_output_edge_index(out, v_out[5], v_out[7]);
765  EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1);
766  EXPECT_TRUE(output_edge_has_input_id(out, e_cross_1, 8));
767  EXPECT_TRUE(output_edge_has_input_id(out, e_cross_2, 8));
768  EXPECT_TRUE(output_edge_has_input_id(out, e_cross_3, 8));
769  }
770  if (DO_DRAW) {
771  graph_draw<T>("TwoDiamondsCross", out.vert, out.edge, out.face);
772  }
773 }
774 
775 template<typename T> void manycross_test()
776 {
777  /* Input has some repetition of vertices, on purpose */
778  const char *spec = R"(27 21 0
779  0.0 0.0
780  6.0 9.0
781  15.0 18.0
782  35.0 13.0
783  43.0 18.0
784  57.0 12.0
785  69.0 10.0
786  78.0 0.0
787  91.0 0.0
788  107.0 22.0
789  123.0 0.0
790  0.0 0.0
791  10.0 -14.0
792  35.0 -8.0
793  43.0 -12.0
794  64.0 -13.0
795  78.0 0.0
796  91.0 0.0
797  102.0 -9.0
798  116.0 -9.0
799  123.0 0.0
800  43.0 18.0
801  43.0 -12.0
802  107.0 22.0
803  102.0 -9.0
804  0.0 0.0
805  123.0 0.0
806  0 1
807  1 2
808  2 3
809  3 4
810  4 5
811  5 6
812  6 7
813  7 8
814  8 9
815  9 10
816  11 12
817  12 13
818  13 14
819  14 15
820  15 16
821  17 18
822  18 19
823  19 20
824  21 22
825  23 24
826  25 26
827  )";
828 
829  CDT_input<T> in = fill_input_from_string<T>(spec);
830  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
831  EXPECT_EQ(out.vert.size(), 19);
832  EXPECT_EQ(out.edge.size(), 46);
833  EXPECT_EQ(out.face.size(), 28);
834  if (DO_DRAW) {
835  graph_draw<T>("ManyCross", out.vert, out.edge, out.face);
836  }
837 }
838 
839 template<typename T> void twoface_test()
840 {
841  const char *spec = R"(6 0 2
842  0.0 0.0
843  1.0 0.0
844  0.5 1.0
845  1.1 1.0
846  1.1 0.0
847  1.6 1.0
848  0 1 2
849  3 4 5
850  )";
851 
852  CDT_input<T> in = fill_input_from_string<T>(spec);
853  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
854  EXPECT_EQ(out.vert.size(), 6);
855  EXPECT_EQ(out.edge.size(), 9);
856  EXPECT_EQ(out.face.size(), 4);
857  if (out.vert.size() == 6 && out.edge.size() == 9 && out.face.size() == 4) {
858  int v_out[6];
859  for (int i = 0; i < 6; i++) {
860  v_out[i] = get_orig_index(out.vert_orig, i);
861  EXPECT_NE(v_out[i], -1);
862  }
863  int f0_out = get_output_tri_index(out, v_out[0], v_out[1], v_out[2]);
864  int f1_out = get_output_tri_index(out, v_out[3], v_out[4], v_out[5]);
865  EXPECT_NE(f0_out, -1);
866  EXPECT_NE(f1_out, -1);
867  int e0_out = get_output_edge_index(out, v_out[0], v_out[1]);
868  int e1_out = get_output_edge_index(out, v_out[1], v_out[2]);
869  int e2_out = get_output_edge_index(out, v_out[2], v_out[0]);
870  EXPECT_NE(e0_out, -1);
871  EXPECT_NE(e1_out, -1);
872  EXPECT_NE(e2_out, -1);
873  EXPECT_TRUE(output_edge_has_input_id(out, e0_out, out.face_edge_offset + 0));
874  EXPECT_TRUE(output_edge_has_input_id(out, e1_out, out.face_edge_offset + 1));
875  EXPECT_TRUE(output_edge_has_input_id(out, e2_out, out.face_edge_offset + 2));
876  EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0));
877  EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1));
878  }
879  if (DO_DRAW) {
880  graph_draw<T>("TwoFace", out.vert, out.edge, out.face);
881  }
882 }
883 
884 template<typename T> void twoface2_test()
885 {
886  const char *spec = R"(6 0 2
887  0.0 0.0
888  4.0 4.0
889  -4.0 2.0
890  3.0 0.0
891  3.0 6.0
892  -1.0 2.0
893  0 1 2
894  3 4 5
895  )";
896 
897  CDT_input<T> in = fill_input_from_string<T>(spec);
898  CDT_result<T> out = delaunay_2d_calc(in, CDT_INSIDE);
899  EXPECT_EQ(out.vert.size(), 10);
900  EXPECT_EQ(out.edge.size(), 18);
901  EXPECT_EQ(out.face.size(), 9);
902  if (out.vert.size() == 10 && out.edge.size() == 18 && out.face.size() == 9) {
903  /* Input verts have no dups, so expect output ones match input ones. */
904  for (int i = 0; i < 6; i++) {
905  EXPECT_EQ(get_orig_index(out.vert_orig, i), i);
906  }
907  int v6 = get_vertex_by_coord(out, 3.0, 3.0);
908  EXPECT_NE(v6, -1);
909  int v7 = get_vertex_by_coord(out, 3.0, 3.75);
910  EXPECT_NE(v7, -1);
911  int v8 = get_vertex_by_coord(out, 0.0, 3.0);
912  EXPECT_NE(v8, -1);
913  int v9 = get_vertex_by_coord(out, 1.0, 1.0);
914  EXPECT_NE(v9, -1);
915  /* f0 to f3 should be triangles part of input face 0, not part of input face 1. */
916  int f0 = get_output_tri_index(out, 0, 9, 5);
917  EXPECT_NE(f0, -1);
918  EXPECT_TRUE(output_face_has_input_id(out, f0, 0));
919  EXPECT_FALSE(output_face_has_input_id(out, f0, 1));
920  int f1 = get_output_tri_index(out, 0, 5, 2);
921  EXPECT_NE(f1, -1);
922  EXPECT_TRUE(output_face_has_input_id(out, f1, 0));
923  EXPECT_FALSE(output_face_has_input_id(out, f1, 1));
924  int f2 = get_output_tri_index(out, 2, 5, 8);
925  EXPECT_NE(f2, -1);
926  EXPECT_TRUE(output_face_has_input_id(out, f2, 0));
927  EXPECT_FALSE(output_face_has_input_id(out, f2, 1));
928  int f3 = get_output_tri_index(out, 6, 1, 7);
929  EXPECT_NE(f3, -1);
930  EXPECT_TRUE(output_face_has_input_id(out, f3, 0));
931  EXPECT_FALSE(output_face_has_input_id(out, f3, 1));
932  /* f4 and f5 should be triangles part of input face 1, not part of input face 0. */
933  int f4 = get_output_tri_index(out, 8, 7, 4);
934  EXPECT_NE(f4, -1);
935  EXPECT_FALSE(output_face_has_input_id(out, f4, 0));
936  EXPECT_TRUE(output_face_has_input_id(out, f4, 1));
937  int f5 = get_output_tri_index(out, 3, 6, 9);
938  EXPECT_NE(f5, -1);
939  EXPECT_FALSE(output_face_has_input_id(out, f5, 0));
940  EXPECT_TRUE(output_face_has_input_id(out, f5, 1));
941  /* f6 to f8 should be triangles part of both input faces. */
942  int f6 = get_output_tri_index(out, 5, 9, 6);
943  EXPECT_NE(f6, -1);
944  EXPECT_TRUE(output_face_has_input_id(out, f6, 0));
945  EXPECT_TRUE(output_face_has_input_id(out, f6, 1));
946  int f7 = get_output_tri_index(out, 5, 6, 7);
947  EXPECT_NE(f7, -1);
948  EXPECT_TRUE(output_face_has_input_id(out, f7, 0));
949  EXPECT_TRUE(output_face_has_input_id(out, f7, 1));
950  int f8 = get_output_tri_index(out, 5, 7, 8);
951  EXPECT_NE(f8, -1);
952  EXPECT_TRUE(output_face_has_input_id(out, f8, 0));
953  EXPECT_TRUE(output_face_has_input_id(out, f8, 1));
954  }
955  if (DO_DRAW) {
956  graph_draw<T>("TwoFace2", out.vert, out.edge, out.face);
957  }
958 }
959 
960 template<typename T> void overlapfaces_test()
961 {
962  const char *spec = R"(12 0 3
963  0.0 0.0
964  1.0 0.0
965  1.0 1.0
966  0.0 1.0
967  0.5 0.5
968  1.5 0.5
969  1.5 1.3
970  0.5 1.3
971  0.1 0.1
972  0.3 0.1
973  0.3 0.3
974  0.1 0.3
975  0 1 2 3
976  4 5 6 7
977  8 9 10 11
978  )";
979 
980  CDT_input<T> in = fill_input_from_string<T>(spec);
981  CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
982  EXPECT_EQ(out.vert.size(), 14);
983  EXPECT_EQ(out.edge.size(), 33);
984  EXPECT_EQ(out.face.size(), 20);
985  if (out.vert.size() == 14 && out.edge.size() == 33 && out.face.size() == 20) {
986  int v_out[12];
987  for (int i = 0; i < 12; i++) {
988  v_out[i] = get_orig_index(out.vert_orig, i);
989  EXPECT_NE(v_out[i], -1);
990  }
991  int v_int1 = 12;
992  int v_int2 = 13;
993  T x = out.vert[v_int1][0] - T(1);
994  if (math_abs(x) > in.epsilon) {
995  v_int1 = 13;
996  v_int2 = 12;
997  }
998  expect_coord_near<T>(out.vert[v_int1], vec2<T>(1, 0.5));
999  expect_coord_near<T>(out.vert[v_int2], vec2<T>(0.5, 1));
1000  EXPECT_EQ(out.vert_orig[v_int1].size(), 0);
1001  EXPECT_EQ(out.vert_orig[v_int2].size(), 0);
1002  int f0_out = get_output_tri_index(out, v_out[1], v_int1, v_out[4]);
1003  EXPECT_NE(f0_out, -1);
1004  EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0));
1005  int f1_out = get_output_tri_index(out, v_out[4], v_int1, v_out[2]);
1006  EXPECT_NE(f1_out, -1);
1007  EXPECT_TRUE(output_face_has_input_id(out, f1_out, 0));
1008  EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1));
1009  int f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[10]);
1010  if (f2_out == -1) {
1011  f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[11]);
1012  }
1013  EXPECT_NE(f2_out, -1);
1014  EXPECT_TRUE(output_face_has_input_id(out, f2_out, 0));
1015  EXPECT_TRUE(output_face_has_input_id(out, f2_out, 2));
1016  }
1017  if (DO_DRAW) {
1018  graph_draw<T>("OverlapFaces - full", out.vert, out.edge, out.face);
1019  }
1020 
1021  /* Different output types. */
1022  CDT_result<T> out2 = delaunay_2d_calc(in, CDT_INSIDE);
1023  EXPECT_EQ(out2.face.size(), 18);
1024  if (DO_DRAW) {
1025  graph_draw<T>("OverlapFaces - inside", out2.vert, out2.edge, out2.face);
1026  }
1027 
1028  CDT_result<T> out3 = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1029  EXPECT_EQ(out3.face.size(), 4);
1030  if (DO_DRAW) {
1031  graph_draw<T>("OverlapFaces - constraints", out3.vert, out3.edge, out3.face);
1032  }
1033 
1034  CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
1035  EXPECT_EQ(out4.face.size(), 5);
1036  if (DO_DRAW) {
1037  graph_draw<T>("OverlapFaces - valid bmesh", out4.vert, out4.edge, out4.face);
1038  }
1039 }
1040 
1041 template<typename T> void twosquaresoverlap_test()
1042 {
1043  const char *spec = R"(8 0 2
1044  1.0 -1.0
1045  -1.0 -1.0
1046  -1.0 1.0
1047  1.0 1.0
1048  -1.5 1.5
1049  0.5 1.5
1050  0.5 -0.5
1051  -1.5 -0.5
1052  7 6 5 4
1053  3 2 1 0
1054  )";
1055 
1056  CDT_input<T> in = fill_input_from_string<T>(spec);
1057  CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
1058  EXPECT_EQ(out.vert.size(), 10);
1059  EXPECT_EQ(out.edge.size(), 12);
1060  EXPECT_EQ(out.face.size(), 3);
1061  if (DO_DRAW) {
1062  graph_draw<T>("TwoSquaresOverlap", out.vert, out.edge, out.face);
1063  }
1064 }
1065 
1066 template<typename T> void twofaceedgeoverlap_test()
1067 {
1068  const char *spec = R"(6 0 2
1069  5.657 0.0
1070  -1.414 -5.831
1071  0.0 0.0
1072  5.657 0.0
1073  -2.121 -2.915
1074  0.0 0.0
1075  2 1 0
1076  5 4 3
1077  )";
1078 
1079  CDT_input<T> in = fill_input_from_string<T>(spec);
1080  CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1081  EXPECT_EQ(out.vert.size(), 5);
1082  EXPECT_EQ(out.edge.size(), 7);
1083  EXPECT_EQ(out.face.size(), 3);
1084  if (out.vert.size() == 5 && out.edge.size() == 7 && out.face.size() == 3) {
1085  int v_int = 4;
1086  int v_out[6];
1087  for (int i = 0; i < 6; i++) {
1088  v_out[i] = get_orig_index(out.vert_orig, i);
1089  EXPECT_NE(v_out[i], -1);
1090  EXPECT_NE(v_out[i], v_int);
1091  }
1092  EXPECT_EQ(v_out[0], v_out[3]);
1093  EXPECT_EQ(v_out[2], v_out[5]);
1094  int e01 = get_output_edge_index(out, v_out[0], v_out[1]);
1095  int foff = out.face_edge_offset;
1096  EXPECT_TRUE(output_edge_has_input_id(out, e01, foff + 1));
1097  int e1i = get_output_edge_index(out, v_out[1], v_int);
1098  EXPECT_TRUE(output_edge_has_input_id(out, e1i, foff + 0));
1099  int ei2 = get_output_edge_index(out, v_int, v_out[2]);
1100  EXPECT_TRUE(output_edge_has_input_id(out, ei2, foff + 0));
1101  int e20 = get_output_edge_index(out, v_out[2], v_out[0]);
1102  EXPECT_TRUE(output_edge_has_input_id(out, e20, foff + 2));
1103  EXPECT_TRUE(output_edge_has_input_id(out, e20, 2 * foff + 2));
1104  int e24 = get_output_edge_index(out, v_out[2], v_out[4]);
1105  EXPECT_TRUE(output_edge_has_input_id(out, e24, 2 * foff + 0));
1106  int e4i = get_output_edge_index(out, v_out[4], v_int);
1107  EXPECT_TRUE(output_edge_has_input_id(out, e4i, 2 * foff + 1));
1108  int ei0 = get_output_edge_index(out, v_int, v_out[0]);
1109  EXPECT_TRUE(output_edge_has_input_id(out, ei0, 2 * foff + 1));
1110  int f02i = get_output_tri_index(out, v_out[0], v_out[2], v_int);
1111  EXPECT_NE(f02i, -1);
1112  EXPECT_TRUE(output_face_has_input_id(out, f02i, 0));
1113  EXPECT_TRUE(output_face_has_input_id(out, f02i, 1));
1114  int f24i = get_output_tri_index(out, v_out[2], v_out[4], v_int);
1115  EXPECT_NE(f24i, -1);
1116  EXPECT_TRUE(output_face_has_input_id(out, f24i, 1));
1117  EXPECT_FALSE(output_face_has_input_id(out, f24i, 0));
1118  int f10i = get_output_tri_index(out, v_out[1], v_out[0], v_int);
1119  EXPECT_NE(f10i, -1);
1120  EXPECT_TRUE(output_face_has_input_id(out, f10i, 0));
1121  EXPECT_FALSE(output_face_has_input_id(out, f10i, 1));
1122  }
1123  if (DO_DRAW) {
1124  graph_draw<T>("TwoFaceEdgeOverlap", out.vert, out.edge, out.face);
1125  }
1126 }
1127 
1128 template<typename T> void triintri_test()
1129 {
1130  const char *spec = R"(6 0 2
1131  -5.65685 0.0
1132  1.41421 -5.83095
1133  0.0 0.0
1134  -2.47487 -1.45774
1135  -0.707107 -2.91548
1136  -1.06066 -1.45774
1137  0 1 2
1138  3 4 5
1139  )";
1140 
1141  CDT_input<T> in = fill_input_from_string<T>(spec);
1142  CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
1143  EXPECT_EQ(out.vert.size(), 6);
1144  EXPECT_EQ(out.edge.size(), 8);
1145  EXPECT_EQ(out.face.size(), 3);
1146  if (DO_DRAW) {
1147  graph_draw<T>("TriInTri", out.vert, out.edge, out.face);
1148  }
1149 }
1150 
1151 template<typename T> void diamondinsquare_test()
1152 {
1153  const char *spec = R"(8 0 2
1154  0.0 0.0
1155  1.0 0.0
1156  1.0 1.0
1157  0.0 1.0
1158  0.14644660940672627 0.5
1159  0.5 0.14644660940672627
1160  0.8535533905932737 0.5
1161  0.5 0.8535533905932737
1162  0 1 2 3
1163  4 5 6 7
1164  )";
1165 
1166  CDT_input<T> in = fill_input_from_string<T>(spec);
1167  CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
1168  EXPECT_EQ(out.vert.size(), 8);
1169  EXPECT_EQ(out.edge.size(), 10);
1170  EXPECT_EQ(out.face.size(), 3);
1171  if (DO_DRAW) {
1172  graph_draw<T>("DiamondInSquare", out.vert, out.edge, out.face);
1173  }
1174 }
1175 
1176 template<typename T> void diamondinsquarewire_test()
1177 {
1178  const char *spec = R"(8 8 0
1179  0.0 0.0
1180  1.0 0.0
1181  1.0 1.0
1182  0.0 1.0
1183  0.14644660940672627 0.5
1184  0.5 0.14644660940672627
1185  0.8535533905932737 0.5
1186  0.5 0.8535533905932737
1187  0 1
1188  1 2
1189  2 3
1190  3 0
1191  4 5
1192  5 6
1193  6 7
1194  7 4
1195  )";
1196 
1197  CDT_input<T> in = fill_input_from_string<T>(spec);
1198  CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1199  EXPECT_EQ(out.vert.size(), 8);
1200  EXPECT_EQ(out.edge.size(), 8);
1201  EXPECT_EQ(out.face.size(), 2);
1202  if (DO_DRAW) {
1203  graph_draw<T>("DiamondInSquareWire", out.vert, out.edge, out.face);
1204  }
1205 }
1206 
1207 template<typename T> void repeatedge_test()
1208 {
1209  const char *spec = R"(5 3 0
1210  0.0 0.0
1211  0.0 1.0
1212  1.0 1.1
1213  0.5 -0.5
1214  0.5 2.5
1215  0 1
1216  2 3
1217  2 3
1218  )";
1219 
1220  CDT_input<T> in = fill_input_from_string<T>(spec);
1221  CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1222  EXPECT_EQ(out.edge.size(), 2);
1223  if (DO_DRAW) {
1224  graph_draw<T>("RepeatEdge", out.vert, out.edge, out.face);
1225  }
1226 }
1227 
1228 template<typename T> void repeattri_test()
1229 {
1230  const char *spec = R"(3 0 2
1231  0.0 0.0
1232  1.0 0.0
1233  0.5 1.0
1234  0 1 2
1235  0 1 2
1236  )";
1237 
1238  CDT_input<T> in = fill_input_from_string<T>(spec);
1239  CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1240  EXPECT_EQ(out.edge.size(), 3);
1241  EXPECT_EQ(out.face.size(), 1);
1242  EXPECT_TRUE(output_face_has_input_id(out, 0, 0));
1243  EXPECT_TRUE(output_face_has_input_id(out, 0, 1));
1244  if (DO_DRAW) {
1245  graph_draw<T>("RepeatTri", out.vert, out.edge, out.face);
1246  }
1247 }
1248 
1249 TEST(delaunay_d, Empty)
1250 {
1251  empty_test<double>();
1252 }
1253 
1254 TEST(delaunay_d, OnePt)
1255 {
1256  onept_test<double>();
1257 }
1258 
1259 TEST(delaunay_d, TwoPt)
1260 {
1261  twopt_test<double>();
1262 }
1263 
1264 TEST(delaunay_d, ThreePt)
1265 {
1266  threept_test<double>();
1267 }
1268 
1269 TEST(delaunay_d, MixedPts)
1270 {
1271  mixedpts_test<double>();
1272 }
1273 
1274 TEST(delaunay_d, Quad0)
1275 {
1276  quad0_test<double>();
1277 }
1278 
1279 TEST(delaunay_d, Quad1)
1280 {
1281  quad1_test<double>();
1282 }
1283 
1284 TEST(delaunay_d, Quad2)
1285 {
1286  quad2_test<double>();
1287 }
1288 
1289 TEST(delaunay_d, Quad3)
1290 {
1291  quad3_test<double>();
1292 }
1293 
1294 TEST(delaunay_d, Quad4)
1295 {
1296  quad4_test<double>();
1297 }
1298 
1299 TEST(delaunay_d, LineInSquare)
1300 {
1301  lineinsquare_test<double>();
1302 }
1303 
1304 TEST(delaunay_d, CrossSegs)
1305 {
1306  crosssegs_test<double>();
1307 }
1308 
1309 TEST(delaunay_d, DiamondCross)
1310 {
1311  diamondcross_test<double>();
1312 }
1313 
1314 TEST(delaunay_d, TwoDiamondsCross)
1315 {
1316  twodiamondscross_test<double>();
1317 }
1318 
1319 TEST(delaunay_d, ManyCross)
1320 {
1321  manycross_test<double>();
1322 }
1323 
1324 TEST(delaunay_d, TwoFace)
1325 {
1326  twoface_test<double>();
1327 }
1328 
1329 TEST(delaunay_d, TwoFace2)
1330 {
1331  twoface2_test<double>();
1332 }
1333 
1334 TEST(delaunay_d, OverlapFaces)
1335 {
1336  overlapfaces_test<double>();
1337 }
1338 
1339 TEST(delaunay_d, TwoSquaresOverlap)
1340 {
1341  twosquaresoverlap_test<double>();
1342 }
1343 
1344 TEST(delaunay_d, TwoFaceEdgeOverlap)
1345 {
1346  twofaceedgeoverlap_test<double>();
1347 }
1348 
1349 TEST(delaunay_d, TriInTri)
1350 {
1351  triintri_test<double>();
1352 }
1353 
1354 TEST(delaunay_d, DiamondInSquare)
1355 {
1356  diamondinsquare_test<double>();
1357 }
1358 
1359 TEST(delaunay_d, DiamondInSquareWire)
1360 {
1361  diamondinsquarewire_test<double>();
1362 }
1363 
1364 TEST(delaunay_d, RepeatEdge)
1365 {
1366  repeatedge_test<double>();
1367 }
1368 
1369 TEST(delaunay_d, RepeatTri)
1370 {
1371  repeattri_test<double>();
1372 }
1373 
1374 # ifdef WITH_GMP
1375 TEST(delaunay_m, Empty)
1376 {
1377  empty_test<mpq_class>();
1378 }
1379 
1380 TEST(delaunay_m, OnePt)
1381 {
1382  onept_test<mpq_class>();
1383 }
1384 TEST(delaunay_m, TwoPt)
1385 {
1386  twopt_test<mpq_class>();
1387 }
1388 
1389 TEST(delaunay_m, ThreePt)
1390 {
1391  threept_test<mpq_class>();
1392 }
1393 
1394 TEST(delaunay_m, MixedPts)
1395 {
1396  mixedpts_test<mpq_class>();
1397 }
1398 
1399 TEST(delaunay_m, Quad0)
1400 {
1401  quad0_test<mpq_class>();
1402 }
1403 
1404 TEST(delaunay_m, Quad1)
1405 {
1406  quad1_test<mpq_class>();
1407 }
1408 
1409 TEST(delaunay_m, Quad2)
1410 {
1411  quad2_test<mpq_class>();
1412 }
1413 
1414 TEST(delaunay_m, Quad3)
1415 {
1416  quad3_test<mpq_class>();
1417 }
1418 
1419 TEST(delaunay_m, Quad4)
1420 {
1421  quad4_test<mpq_class>();
1422 }
1423 
1424 TEST(delaunay_m, LineInSquare)
1425 {
1426  lineinsquare_test<mpq_class>();
1427 }
1428 
1429 TEST(delaunay_m, CrossSegs)
1430 {
1431  crosssegs_test<mpq_class>();
1432 }
1433 
1434 TEST(delaunay_m, DiamondCross)
1435 {
1436  diamondcross_test<mpq_class>();
1437 }
1438 
1439 TEST(delaunay_m, TwoDiamondsCross)
1440 {
1441  twodiamondscross_test<mpq_class>();
1442 }
1443 
1444 TEST(delaunay_m, ManyCross)
1445 {
1446  manycross_test<mpq_class>();
1447 }
1448 
1449 TEST(delaunay_m, TwoFace)
1450 {
1451  twoface_test<mpq_class>();
1452 }
1453 
1454 TEST(delaunay_m, TwoFace2)
1455 {
1456  twoface2_test<mpq_class>();
1457 }
1458 
1459 TEST(delaunay_m, OverlapFaces)
1460 {
1461  overlapfaces_test<mpq_class>();
1462 }
1463 
1464 TEST(delaunay_m, TwoSquaresOverlap)
1465 {
1466  twosquaresoverlap_test<mpq_class>();
1467 }
1468 
1469 TEST(delaunay_m, TwoFaceEdgeOverlap)
1470 {
1471  twofaceedgeoverlap_test<mpq_class>();
1472 }
1473 
1474 TEST(delaunay_m, TriInTri)
1475 {
1476  triintri_test<mpq_class>();
1477 }
1478 
1479 TEST(delaunay_m, DiamondInSquare)
1480 {
1481  diamondinsquare_test<mpq_class>();
1482 }
1483 
1484 TEST(delaunay_m, DiamondInSquareWire)
1485 {
1486  diamondinsquarewire_test<mpq_class>();
1487 }
1488 
1489 TEST(delaunay_m, RepeatEdge)
1490 {
1491  repeatedge_test<mpq_class>();
1492 }
1493 
1494 TEST(delaunay_m, RepeatTri)
1495 {
1496  repeattri_test<mpq_class>();
1497 }
1498 # endif
1499 
1500 #endif
1501 
1502 #if DO_C_TESTS
1503 
1504 TEST(delaunay_d, CintTwoFace)
1505 {
1506  float vert_coords[][2] = {
1507  {0.0, 0.0}, {1.0, 0.0}, {0.5, 1.0}, {1.1, 1.0}, {1.1, 0.0}, {1.6, 1.0}};
1508  int faces[] = {0, 1, 2, 3, 4, 5};
1509  int faces_len[] = {3, 3};
1510  int faces_start[] = {0, 3};
1511 
1512  ::CDT_input input;
1513  input.verts_len = 6;
1514  input.edges_len = 0;
1515  input.faces_len = 2;
1516  input.vert_coords = vert_coords;
1517  input.edges = nullptr;
1518  input.faces = faces;
1519  input.faces_len_table = faces_len;
1520  input.faces_start_table = faces_start;
1521  input.epsilon = 1e-5f;
1524 }
1525 #endif
1526 
1527 #if DO_RANDOM_TESTS
1528 
1529 enum {
1530  RANDOM_PTS,
1531  RANDOM_SEGS,
1532  RANDOM_POLY,
1533  RANDOM_TILTED_GRID,
1534  RANDOM_CIRCLE,
1535  RANDOM_TRI_BETWEEN_CIRCLES,
1536 };
1537 
1538 template<typename T>
1539 void rand_delaunay_test(int test_kind,
1540  int start_lg_size,
1541  int max_lg_size,
1542  int reps_per_size,
1543  double param,
1544  CDT_output_type otype)
1545 {
1546  constexpr bool print_timing = true;
1547  RNG *rng = BLI_rng_new(0);
1548  Array<double> times(max_lg_size + 1);
1549 
1550  /* For powers of 2 sizes up to max_lg_size power of 2. */
1551  for (int lg_size = start_lg_size; lg_size <= max_lg_size; ++lg_size) {
1552  int size = 1 << lg_size;
1553  times[lg_size] = 0.0;
1554  if (size == 1 && test_kind != RANDOM_PTS) {
1555  continue;
1556  }
1557  /* Do 'rep' repetitions. */
1558  for (int rep = 0; rep < reps_per_size; ++rep) {
1559  /* First use test type and size to set npts, nedges, and nfaces. */
1560  int npts = 0;
1561  int nedges = 0;
1562  int nfaces = 0;
1563  std::string test_label;
1564  switch (test_kind) {
1565  case RANDOM_PTS: {
1566  npts = size;
1567  test_label = std::to_string(npts) + "Random points";
1568  } break;
1569  case RANDOM_SEGS: {
1570  npts = size;
1571  nedges = npts - 1;
1572  test_label = std::to_string(nedges) + "Random edges";
1573  } break;
1574  case RANDOM_POLY: {
1575  npts = size;
1576  nedges = npts;
1577  test_label = "Random poly with " + std::to_string(nedges) + " edges";
1578  } break;
1579  case RANDOM_TILTED_GRID: {
1580  /* A 'size' x 'size' grid of points, tilted by angle 'param'.
1581  * Edges will go from left ends to right ends and tops to bottoms,
1582  * so 2 x size of them.
1583  * Depending on epsilon, the vertical-ish edges may or may not go
1584  * through the intermediate vertices, but the horizontal ones always should.
1585  * 'param' is slope of tilt of vertical lines.
1586  */
1587  npts = size * size;
1588  nedges = 2 * size;
1589  test_label = "Tilted grid " + std::to_string(npts) + "x" + std::to_string(npts) +
1590  " (tilt=" + std::to_string(param) + ")";
1591  } break;
1592  case RANDOM_CIRCLE: {
1593  /* A circle with 'size' points, a random start angle,
1594  * and equal spacing thereafter. Will be input as one face.
1595  */
1596  npts = size;
1597  nfaces = 1;
1598  test_label = "Circle with " + std::to_string(npts) + " points";
1599  } break;
1600  case RANDOM_TRI_BETWEEN_CIRCLES: {
1601  /* A set of 'size' triangles, each has two random points on the unit circle,
1602  * and the third point is a random point on the circle with radius 'param'.
1603  * Each triangle will be input as a face.
1604  */
1605  npts = 3 * size;
1606  nfaces = size;
1607  test_label = "Random " + std::to_string(nfaces) +
1608  " triangles between circles (inner radius=" + std::to_string(param) + ")";
1609  } break;
1610  default:
1611  std::cout << "unknown delaunay test type\n";
1612  return;
1613  }
1614  if (otype != CDT_FULL) {
1615  if (otype == CDT_INSIDE) {
1616  test_label += " (inside)";
1617  }
1618  else if (otype == CDT_CONSTRAINTS) {
1619  test_label += " (constraints)";
1620  }
1621  else if (otype == CDT_CONSTRAINTS_VALID_BMESH) {
1622  test_label += " (valid bmesh)";
1623  }
1624  }
1625 
1626  CDT_input<T> in;
1627  in.vert = Array<vec2<T>>(npts);
1628  if (nedges > 0) {
1629  in.edge = Array<std::pair<int, int>>(nedges);
1630  }
1631  if (nfaces > 0) {
1632  in.face = Array<Vector<int>>(nfaces);
1633  }
1634 
1635  /* Make vertices and edges or faces. */
1636  switch (test_kind) {
1637  case RANDOM_PTS:
1638  case RANDOM_SEGS:
1639  case RANDOM_POLY: {
1640  for (int i = 0; i < size; i++) {
1641  in.vert[i][0] = T(BLI_rng_get_double(rng)); /* will be in range in [0,1) */
1642  in.vert[i][1] = T(BLI_rng_get_double(rng));
1643  if (test_kind != RANDOM_PTS) {
1644  if (i > 0) {
1645  in.edge[i - 1].first = i - 1;
1646  in.edge[i - 1].second = i;
1647  }
1648  }
1649  }
1650  if (test_kind == RANDOM_POLY) {
1651  in.edge[size - 1].first = size - 1;
1652  in.edge[size - 1].second = 0;
1653  }
1654  } break;
1655 
1656  case RANDOM_TILTED_GRID: {
1657  for (int i = 0; i < size; ++i) {
1658  for (int j = 0; j < size; ++j) {
1659  in.vert[i * size + j][0] = T(i * param + j);
1660  in.vert[i * size + j][1] = T(i);
1661  }
1662  }
1663  for (int i = 0; i < size; ++i) {
1664  /* Horizontal edges: connect `p(i,0)` to `p(i,size-1)`. */
1665  in.edge[i].first = i * size;
1666  in.edge[i].second = i * size + size - 1;
1667  /* Vertical edges: connect `p(0,i)` to `p(size-1,i)`. */
1668  in.edge[size + i].first = i;
1669  in.edge[size + i].second = (size - 1) * size + i;
1670  }
1671  } break;
1672 
1673  case RANDOM_CIRCLE: {
1674  double start_angle = BLI_rng_get_double(rng) * 2.0 * M_PI;
1675  double angle_delta = 2.0 * M_PI / size;
1676  for (int i = 0; i < size; i++) {
1677  in.vert[i][0] = T(cos(start_angle + i * angle_delta));
1678  in.vert[i][1] = T(sin(start_angle + i * angle_delta));
1679  in.face[0].append(i);
1680  }
1681  } break;
1682 
1683  case RANDOM_TRI_BETWEEN_CIRCLES: {
1684  for (int i = 0; i < size; i++) {
1685  /* Get three random angles in [0, 2pi). */
1686  double angle1 = BLI_rng_get_double(rng) * 2.0 * M_PI;
1687  double angle2 = BLI_rng_get_double(rng) * 2.0 * M_PI;
1688  double angle3 = BLI_rng_get_double(rng) * 2.0 * M_PI;
1689  int ia = 3 * i;
1690  int ib = 3 * i + 1;
1691  int ic = 3 * i + 2;
1692  in.vert[ia][0] = T(cos(angle1));
1693  in.vert[ia][1] = T(sin(angle1));
1694  in.vert[ib][0] = T(cos(angle2));
1695  in.vert[ib][1] = T(sin(angle2));
1696  in.vert[ic][0] = T((param * cos(angle3)));
1697  in.vert[ic][1] = T((param * sin(angle3)));
1698  /* Put the coordinates in CCW order. */
1699  in.face[i].append(ia);
1700  int orient = orient2d(in.vert[ia], in.vert[ib], in.vert[ic]);
1701  if (orient >= 0) {
1702  in.face[i].append(ib);
1703  in.face[i].append(ic);
1704  }
1705  else {
1706  in.face[i].append(ic);
1707  in.face[i].append(ib);
1708  }
1709  }
1710  } break;
1711  }
1712 
1713  /* Run the test. */
1714  double tstart = PIL_check_seconds_timer();
1715  CDT_result<T> out = delaunay_2d_calc(in, otype);
1716  EXPECT_NE(out.vert.size(), 0);
1717  times[lg_size] += PIL_check_seconds_timer() - tstart;
1718  if (DO_DRAW) {
1719  graph_draw<T>(test_label, out.vert, out.edge, out.face);
1720  }
1721  }
1722  }
1723  if (print_timing) {
1724  std::cout << "\nsize,time\n";
1725  for (int lg_size = 0; lg_size <= max_lg_size; lg_size++) {
1726  int size = 1 << lg_size;
1727  std::cout << size << "," << times[lg_size] << "\n";
1728  }
1729  }
1730  BLI_rng_free(rng);
1731 }
1732 
1733 TEST(delaunay_d, RandomPts)
1734 {
1735  rand_delaunay_test<double>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL);
1736 }
1737 
1738 TEST(delaunay_d, RandomSegs)
1739 {
1740  rand_delaunay_test<double>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL);
1741 }
1742 
1743 TEST(delaunay_d, RandomPoly)
1744 {
1745  rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL);
1746 }
1747 
1748 TEST(delaunay_d, RandomPolyConstraints)
1749 {
1750  rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS);
1751 }
1752 
1753 TEST(delaunay_d, RandomPolyValidBmesh)
1754 {
1755  rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH);
1756 }
1757 
1758 TEST(delaunay_d, Grid)
1759 {
1760  rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL);
1761 }
1762 
1763 TEST(delaunay_d, TiltedGridA)
1764 {
1765  rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL);
1766 }
1767 
1768 TEST(delaunay_d, TiltedGridB)
1769 {
1770  rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL);
1771 }
1772 
1773 TEST(delaunay_d, RandomCircle)
1774 {
1775  rand_delaunay_test<double>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL);
1776 }
1777 
1778 TEST(delaunay_d, RandomTrisCircle)
1779 {
1780  rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL);
1781 }
1782 
1783 TEST(delaunay_d, RandomTrisCircleB)
1784 {
1785  rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL);
1786 }
1787 
1788 # ifdef WITH_GMP
1789 TEST(delaunay_m, RandomPts)
1790 {
1791  rand_delaunay_test<mpq_class>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL);
1792 }
1793 
1794 TEST(delaunay_m, RandomSegs)
1795 {
1796  rand_delaunay_test<mpq_class>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL);
1797 }
1798 
1799 TEST(delaunay_m, RandomPoly)
1800 {
1801  rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL);
1802 }
1803 
1804 TEST(delaunay_d, RandomPolyInside)
1805 {
1806  rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE);
1807 }
1808 
1809 TEST(delaunay_m, RandomPolyInside)
1810 {
1811  rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE);
1812 }
1813 
1814 TEST(delaunay_m, RandomPolyConstraints)
1815 {
1816  rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS);
1817 }
1818 
1819 TEST(delaunay_m, RandomPolyValidBmesh)
1820 {
1821  rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH);
1822 }
1823 
1824 TEST(delaunay_m, Grid)
1825 {
1826  rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL);
1827 }
1828 
1829 TEST(delaunay_m, TiltedGridA)
1830 {
1831  rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL);
1832 }
1833 
1834 TEST(delaunay_m, TiltedGridB)
1835 {
1836  rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL);
1837 }
1838 
1839 TEST(delaunay_m, RandomCircle)
1840 {
1841  rand_delaunay_test<mpq_class>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL);
1842 }
1843 
1844 TEST(delaunay_m, RandomTrisCircle)
1845 {
1846  rand_delaunay_test<mpq_class>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL);
1847 }
1848 
1849 TEST(delaunay_m, RandomTrisCircleB)
1850 {
1851  rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL);
1852 }
1853 # endif
1854 
1855 #endif
1856 
1857 } // namespace blender::meshintersect
#define BLI_assert(a)
Definition: BLI_assert.h:58
CDT_result * BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_type output_type)
CDT_output_type
@ CDT_CONSTRAINTS_VALID_BMESH
@ CDT_CONSTRAINTS
@ CDT_INSIDE
@ CDT_FULL
void BLI_delaunay_2d_cdt_free(CDT_result *result)
#define SY(y)
#define SX(x)
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define M_PI
Definition: BLI_math_base.h:38
Math vector functions needed specifically for mesh intersect and boolean.
Random number functions.
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition: rand.cc:76
struct RNG * BLI_rng_new(unsigned int seed)
Definition: rand.cc:54
double BLI_rng_get_double(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: rand.cc:112
#define UNUSED(x)
#define ELEM(...)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
Read Guarded memory(de)allocation.
Group RGB to Bright Vector Camera Vector Combine Material Light Line Style Layer Add Ambient Diffuse Glossy Refraction Transparent Toon Principled Hair Volume Principled Light Particle Volume Image Sky Noise Wave Voronoi Brick Texture Vector Combine Vertex Separate Vector White RGB Map Separate Set Z Dilate Combine Combine Color Channel Split ID Combine Luminance Directional Alpha Distance Hue Movie Ellipse Bokeh View Corner Anti Mix RGB Hue Separate TEX_NODE_PROC TEX_NODE_PROC TEX_NODE_PROC TEX_NODE_PROC TEX_NODE_PROC Boolean Random Edge Subdivision Point Object Attribute Attribute Attribute Color Attribute Attribute Vector Point Attribute Sample Collection Attribute Attribute Combine Attribute Grid
Platform independent time functions.
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
#define output
int64_t size() const
Definition: BLI_array.hh:258
const char * label
static float verts[][3]
#define T
static char faces[256]
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:319
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:311
void expect_coord_near< double >(const vec2< double > &testco, const vec2< double > &refco)
double math_to_double< double >(const double v)
static int get_orig_index(const Array< Vector< int >> &out_to_orig, int orig_index)
int get_output_edge_index(const CDT_result< T > &out, int out_index_1, int out_index_2)
static double math_to_double(const T UNUSED(v))
void expect_coord_near(const vec2< T > &testco, const vec2< T > &refco)
bool output_edge_has_input_id(const CDT_result< T > &out, int out_edge_index, int in_edge_index)
int get_output_tri_index(const CDT_result< T > &out, int out_index_1, int out_index_2, int out_index_3)
void graph_draw(const std::string &label, const Array< vec2< T >> &verts, const Array< std::pair< int, int >> &edges, const Array< Vector< int >> &UNUSED(faces))
int get_vertex_by_coord(const CDT_result< T > &out, double x, double y)
bool output_face_has_input_id(const CDT_result< T > &out, int out_face_index, int in_face_index)
std::ostream & operator<<(std::ostream &os, const CDT_result< T > &r)
int get_output_face_index(const CDT_result< T > &out, const Array< int > &poly)
static T math_abs(const T v)
CDT_input< T > fill_input_from_string(const char *spec)
int orient2d(const double2 &a, const double2 &b, const double2 &c)
std::string to_string(const T &n)
float(* vert_coords)[2]
int(* edges)[2]
int * faces_len_table
int * faces_start_table
Definition: rand.cc:48
double PIL_check_seconds_timer(void)
Definition: time.c:80
__forceinline const avxi abs(const avxi &a)
Definition: util_avxi.h:186
ccl_device_inline float2 fabs(const float2 &a)