Blender V4.3
BLI_delaunay_2d_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "testing/testing.h"
6
7#include "MEM_guardedalloc.h"
8
9#include "BLI_rand.h"
10#include "BLI_time.h"
11
12#include <fstream>
13#include <iostream>
14#include <sstream>
15#include <type_traits>
16
17#define DO_CPP_TESTS 1
18#define DO_TEXT_TESTS 0
19#define DO_RANDOM_TESTS 0
20
21#include "BLI_array.hh"
22#include "BLI_math_boolean.hh"
23#include "BLI_math_mpq.hh"
25#include "BLI_vector.hh"
26
27#include "BLI_delaunay_2d.hh"
28
29namespace blender::meshintersect {
30
31/* The spec should have the form:
32 * #verts #edges #faces
33 * <float> <float> [#verts lines)
34 * <int> <int> [#edges lines]
35 * <int> <int> ... <int> [#faces lines]
36 */
37template<typename T> CDT_input<T> fill_input_from_string(const char *spec)
38{
39 std::istringstream ss(spec);
40 std::string line;
41 getline(ss, line);
42 std::istringstream hdrss(line);
43 int nverts, nedges, nfaces;
44 hdrss >> nverts >> nedges >> nfaces;
45 if (nverts == 0) {
46 return CDT_input<T>();
47 }
49 Array<std::pair<int, int>> edges(nedges);
50 Array<Vector<int>> faces(nfaces);
51 int i = 0;
52 while (i < nverts && getline(ss, line)) {
53 std::istringstream iss(line);
54 double dp0, dp1;
55 iss >> dp0 >> dp1;
56 T p0(dp0);
57 T p1(dp1);
58 verts[i] = VecBase<T, 2>(p0, p1);
59 i++;
60 }
61 i = 0;
62 while (i < nedges && getline(ss, line)) {
63 std::istringstream ess(line);
64 int e0, e1;
65 ess >> e0 >> e1;
66 edges[i] = std::pair<int, int>(e0, e1);
67 i++;
68 }
69 i = 0;
70 while (i < nfaces && getline(ss, line)) {
71 std::istringstream fss(line);
72 int v;
73 while (fss >> v) {
74 faces[i].append(v);
75 }
76 i++;
77 }
78 CDT_input<T> ans;
79 ans.vert = verts;
80 ans.edge = edges;
81 ans.face = faces;
82#ifdef WITH_GMP
83 if (std::is_same<mpq_class, T>::value) {
84 ans.epsilon = T(0);
85 }
86 else {
87 ans.epsilon = T(0.00001);
88 }
89#else
90 ans.epsilon = T(0.00001);
91#endif
92 return ans;
93}
94
95/* Find an original index in a table mapping new to original.
96 * Return -1 if not found.
97 */
98static int get_orig_index(const Span<Vector<int>> out_to_orig, int orig_index)
99{
100 int n = int(out_to_orig.size());
101 for (int i = 0; i < n; ++i) {
102 for (int orig : out_to_orig[i]) {
103 if (orig == orig_index) {
104 return i;
105 }
106 }
107 }
108 return -1;
109}
110
111template<typename T> static double math_to_double(const T /*v*/)
112{
113 BLI_assert(false); /* Need implementation for other type. */
114 return 0.0;
115}
116
117template<> double math_to_double<double>(const double v)
118{
119 return v;
120}
121
122#ifdef WITH_GMP
123template<> double math_to_double<mpq_class>(const mpq_class v)
124{
125 return v.get_d();
126}
127#endif
128
129template<typename T> static T math_abs(const T v);
130
131#ifdef WITH_GMP
132template<> mpq_class math_abs(const mpq_class v)
133{
134 return abs(v);
135}
136#endif
137
138template<> double math_abs(const double v)
139{
140 return fabs(v);
141}
142
143/* Find an output index corresponding to a given coordinate (approximately).
144 * Return -1 if not found.
145 */
146template<typename T> int get_vertex_by_coord(const CDT_result<T> &out, double x, double y)
147{
148 int nv = int(out.vert.size());
149 for (int i = 0; i < nv; ++i) {
150 double vx = math_to_double(out.vert[i][0]);
151 double vy = math_to_double(out.vert[i][1]);
152 if (fabs(vx - x) <= 1e-5 && fabs(vy - y) <= 1e-5) {
153 return i;
154 }
155 }
156 return -1;
157}
158
159/* Find an edge between two given output vertex indices. -1 if not found, */
160template<typename T>
161int get_output_edge_index(const CDT_result<T> &out, int out_index_1, int out_index_2)
162{
163 int ne = int(out.edge.size());
164 for (int i = 0; i < ne; ++i) {
165 if ((out.edge[i].first == out_index_1 && out.edge[i].second == out_index_2) ||
166 (out.edge[i].first == out_index_2 && out.edge[i].second == out_index_1))
167 {
168 return i;
169 }
170 }
171 return -1;
172}
173
174template<typename T>
175bool output_edge_has_input_id(const CDT_result<T> &out, int out_edge_index, int in_edge_index)
176{
177 return out_edge_index < int(out.edge_orig.size()) &&
178 out.edge_orig[out_edge_index].contains(in_edge_index);
179}
180
181/* Which out face is for a give output vertex ngon? -1 if not found.
182 * Allow for cyclic shifts vertices of one poly vs the other.
183 */
184template<typename T> int get_output_face_index(const CDT_result<T> &out, const Array<int> &poly)
185{
186 int nf = int(out.face.size());
187 int npolyv = int(poly.size());
188 for (int f = 0; f < nf; ++f) {
189 if (out.face[f].size() != poly.size()) {
190 continue;
191 }
192 for (int cycle_start = 0; cycle_start < npolyv; ++cycle_start) {
193 bool ok = true;
194 for (int k = 0; ok && k < npolyv; ++k) {
195 if (out.face[f][(cycle_start + k) % npolyv] != poly[k]) {
196 ok = false;
197 }
198 }
199 if (ok) {
200 return f;
201 }
202 }
203 }
204 return -1;
205}
206
207template<typename T>
209 int out_index_1,
210 int out_index_2,
211 int out_index_3)
212{
213 Array<int> tri{out_index_1, out_index_2, out_index_3};
214 return get_output_face_index(out, tri);
215}
216
217template<typename T>
218bool output_face_has_input_id(const CDT_result<T> &out, int out_face_index, int in_face_index)
219{
220 return out_face_index < int(out.face_orig.size()) &&
221 out.face_orig[out_face_index].contains(in_face_index);
222}
223
224/* For debugging. */
225template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_result<T> &r)
226{
227 os << "\nRESULT\n";
228 os << r.vert.size() << " verts, " << r.edge.size() << " edges, " << r.face.size() << " faces\n";
229 os << "\nVERTS\n";
230 for (int i : r.vert.index_range()) {
231 os << "v" << i << " = " << r.vert[i] << "\n";
232 os << " orig: ";
233 for (int j : r.vert_orig[i].index_range()) {
234 os << r.vert_orig[i][j] << " ";
235 }
236 os << "\n";
237 }
238 os << "\nEDGES\n";
239 for (int i : r.edge.index_range()) {
240 os << "e" << i << " = (" << r.edge[i].first << ", " << r.edge[i].second << ")\n";
241 os << " orig: ";
242 for (int j : r.edge_orig[i].index_range()) {
243 os << r.edge_orig[i][j] << " ";
244 }
245 os << "\n";
246 }
247 os << "\nFACES\n";
248 for (int i : r.face.index_range()) {
249 os << "f" << i << " = ";
250 for (int j : r.face[i].index_range()) {
251 os << r.face[i][j] << " ";
252 }
253 os << "\n";
254 os << " orig: ";
255 for (int j : r.face_orig[i].index_range()) {
256 os << r.face_orig[i][j] << " ";
257 }
258 os << "\n";
259 }
260 return os;
261}
262
263static bool draw_append = false; /* Will be set to true after first call. */
264
265template<typename T>
266void graph_draw(const std::string &label,
267 const Span<VecBase<T, 2>> verts,
268 const Span<std::pair<int, int>> edges,
269 const Span<Vector<int>> faces)
270{
271 /* Would like to use BKE_tempdir_base() here, but that brings in dependence on kernel library.
272 * This is just for developer debugging anyway, and should never be called in production Blender.
273 */
274#ifdef WIN32
275 constexpr const char *drawfile = "./cdt_test_draw.html";
276#else
277 constexpr const char *drawfile = "/tmp/cdt_test_draw.html";
278#endif
279 constexpr int max_draw_width = 1400;
280 constexpr int max_draw_height = 1000;
281 constexpr int thin_line = 1;
282 constexpr int vert_radius = 3;
283 constexpr bool draw_vert_labels = false;
284 constexpr bool draw_edge_labels = false;
285
286 if (verts.is_empty()) {
287 return;
288 }
289 double2 vmin(1e10, 1e10);
290 double2 vmax(-1e10, -1e10);
291 for (const VecBase<T, 2> &v : verts) {
292 for (int i = 0; i < 2; ++i) {
293 double dvi = math_to_double(v[i]);
294 if (dvi < vmin[i]) {
295 vmin[i] = dvi;
296 }
297 if (dvi > vmax[i]) {
298 vmax[i] = dvi;
299 }
300 }
301 }
302 double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * 0.05;
303 double minx = vmin.x - draw_margin;
304 double maxx = vmax.x + draw_margin;
305 double miny = vmin.y - draw_margin;
306 double maxy = vmax.y + draw_margin;
307
308 double width = maxx - minx;
309 double height = maxy - miny;
310 double aspect = height / width;
311 int view_width = max_draw_width;
312 int view_height = int(view_width * aspect);
313 if (view_height > max_draw_height) {
314 view_height = max_draw_height;
315 view_width = int(view_height / aspect);
316 }
317 double scale = view_width / width;
318
319#define SX(x) ((math_to_double(x) - minx) * scale)
320#define SY(y) ((maxy - math_to_double(y)) * scale)
321
322 std::ofstream f;
323 if (draw_append) {
324 f.open(drawfile, std::ios_base::app);
325 }
326 else {
327 f.open(drawfile);
328 }
329 if (!f) {
330 std::cout << "Could not open file " << drawfile << "\n";
331 return;
332 }
333
334 f << "<div>" << label << "</div>\n<div>\n"
335 << "<svg version=\"1.1\" "
336 "xmlns=\"http://www.w3.org/2000/svg\" "
337 "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
338 "xml:space=\"preserve\"\n"
339 << "width=\"" << view_width << "\" height=\"" << view_height << "\">n";
340
341 for (const Vector<int> &fverts : faces) {
342 f << "<polygon fill=\"azure\" stroke=\"none\"\n points=\"";
343 for (int vi : fverts) {
344 const VecBase<T, 2> &co = verts[vi];
345 f << SX(co[0]) << "," << SY(co[1]) << " ";
346 }
347 f << "\"\n />\n";
348 }
349
350 for (const std::pair<int, int> &e : edges) {
351 const VecBase<T, 2> &uco = verts[e.first];
352 const VecBase<T, 2> &vco = verts[e.second];
353 int strokew = thin_line;
354 f << R"(<line fill="none" stroke="black" stroke-width=")" << strokew << "\" x1=\""
355 << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\""
356 << SY(vco[1]) << "\">\n";
357 f << " <title>[" << e.first << "][" << e.second << "]</title>\n";
358 f << "</line>\n";
359 if (draw_edge_labels) {
360 f << "<text x=\"" << SX(0.5 * (uco[0] + vco[0])) << "\" y=\"" << SY(0.5 * (uco[1] + vco[1]))
361 << R"(" font-size="small">)";
362 f << "[" << e.first << "][" << e.second << "]</text>\n";
363 }
364 }
365
366 int i = 0;
367 for (const VecBase<T, 2> &vco : verts) {
368 f << R"(<circle fill="black" cx=")" << SX(vco[0]) << "\" cy=\"" << SY(vco[1]) << "\" r=\""
369 << vert_radius << "\">\n";
370 f << " <title>[" << i << "]" << vco << "</title>\n";
371 f << "</circle>\n";
372 if (draw_vert_labels) {
373 f << "<text x=\"" << SX(vco[0]) + vert_radius << "\" y=\"" << SY(vco[1]) - vert_radius
374 << R"(" font-size="small">[)" << i << "]</text>\n";
375 }
376 ++i;
377 }
378
380#undef SX
381#undef SY
382}
383
384/* Should tests draw their output to an html file? */
385constexpr bool DO_DRAW = false;
386
387template<typename T>
388void expect_coord_near(const VecBase<T, 2> &testco, const VecBase<T, 2> &refco);
389
390#ifdef WITH_GMP
391template<> void expect_coord_near<mpq_class>(const mpq2 &testco, const mpq2 &refco)
392{
393 EXPECT_EQ(testco[0], refco[0]);
394 EXPECT_EQ(testco[0], refco[0]);
395}
396#endif
397
398template<> void expect_coord_near<double>(const double2 &testco, const double2 &refco)
399{
400 EXPECT_NEAR(testco[0], refco[0], 1e-5);
401 EXPECT_NEAR(testco[1], refco[1], 1e-5);
402}
403
404#if DO_CPP_TESTS
405
406template<typename T> void empty_test()
407{
408 CDT_input<T> in;
409
410 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
411 EXPECT_EQ(0, out.vert.size());
412 EXPECT_EQ(0, out.edge.size());
413 EXPECT_EQ(0, out.face.size());
414 EXPECT_EQ(0, out.vert_orig.size());
415 EXPECT_EQ(0, out.edge_orig.size());
416 EXPECT_EQ(0, out.face_orig.size());
417}
418
419template<typename T> void onept_test()
420{
421 const char *spec = R"(1 0 0
422 0.0 0.0
423 )";
424
425 CDT_input<T> in = fill_input_from_string<T>(spec);
426 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
427 EXPECT_EQ(out.vert.size(), 1);
428 EXPECT_EQ(out.edge.size(), 0);
429 EXPECT_EQ(out.face.size(), 0);
430 if (out.vert.size() >= 1) {
431 expect_coord_near<T>(out.vert[0], VecBase<T, 2>(0, 0));
432 }
433}
434
435template<typename T> void twopt_test()
436{
437 const char *spec = R"(2 0 0
438 0.0 -0.75
439 0.0 0.75
440 )";
441
442 CDT_input<T> in = fill_input_from_string<T>(spec);
443 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
444 EXPECT_EQ(out.vert.size(), 2);
445 EXPECT_EQ(out.edge.size(), 1);
446 EXPECT_EQ(out.face.size(), 0);
447 int v0_out = get_orig_index(out.vert_orig, 0);
448 int v1_out = get_orig_index(out.vert_orig, 1);
449 EXPECT_NE(v0_out, -1);
450 EXPECT_NE(v1_out, -1);
451 EXPECT_NE(v0_out, v1_out);
452 if (out.vert.size() >= 1) {
453 expect_coord_near<T>(out.vert[v0_out], VecBase<T, 2>(0.0, -0.75));
454 expect_coord_near<T>(out.vert[v1_out], VecBase<T, 2>(0.0, 0.75));
455 }
456 int e0_out = get_output_edge_index(out, v0_out, v1_out);
457 EXPECT_EQ(e0_out, 0);
458 if (DO_DRAW) {
459 graph_draw<T>("TwoPt", out.vert, out.edge, out.face);
460 }
461}
462
463template<typename T> void threept_test()
464{
465 const char *spec = R"(3 0 0
466 -0.1 -0.75
467 0.1 0.75
468 0.5 0.5
469 )";
470
471 CDT_input<T> in = fill_input_from_string<T>(spec);
472 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
473 EXPECT_EQ(out.vert.size(), 3);
474 EXPECT_EQ(out.edge.size(), 3);
475 EXPECT_EQ(out.face.size(), 1);
476 int v0_out = get_orig_index(out.vert_orig, 0);
477 int v1_out = get_orig_index(out.vert_orig, 1);
478 int v2_out = get_orig_index(out.vert_orig, 2);
479 EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1);
480 EXPECT_TRUE(v0_out != v1_out && v0_out != v2_out && v1_out != v2_out);
481 int e0_out = get_output_edge_index(out, v0_out, v1_out);
482 int e1_out = get_output_edge_index(out, v1_out, v2_out);
483 int e2_out = get_output_edge_index(out, v2_out, v0_out);
484 EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1);
485 EXPECT_TRUE(e0_out != e1_out && e0_out != e2_out && e1_out != e2_out);
486 int f0_out = get_output_tri_index(out, v0_out, v2_out, v1_out);
487 EXPECT_EQ(f0_out, 0);
488 if (DO_DRAW) {
489 graph_draw<T>("ThreePt", out.vert, out.edge, out.face);
490 }
491}
492
493template<typename T> void mixedpts_test()
494{
495 /* Edges form a chain of length 3. */
496 const char *spec = R"(4 3 0
497 0.0 0.0
498 -0.5 -0.5
499 -0.4 -0.25
500 -0.3 0.8
501 0 1
502 1 2
503 2 3
504 )";
505
506 CDT_input<T> in = fill_input_from_string<T>(spec);
507 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
508 EXPECT_EQ(out.vert.size(), 4);
509 EXPECT_EQ(out.edge.size(), 6);
510 int v0_out = get_orig_index(out.vert_orig, 0);
511 int v1_out = get_orig_index(out.vert_orig, 1);
512 int v2_out = get_orig_index(out.vert_orig, 2);
513 int v3_out = get_orig_index(out.vert_orig, 3);
514 EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1);
515 int e0_out = get_output_edge_index(out, v0_out, v1_out);
516 int e1_out = get_output_edge_index(out, v1_out, v2_out);
517 int e2_out = get_output_edge_index(out, v2_out, v3_out);
518 EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1);
519 EXPECT_TRUE(output_edge_has_input_id(out, e0_out, 0));
520 EXPECT_TRUE(output_edge_has_input_id(out, e1_out, 1));
521 EXPECT_TRUE(output_edge_has_input_id(out, e2_out, 2));
522 if (DO_DRAW) {
523 graph_draw<T>("MixedPts", out.vert, out.edge, out.face);
524 }
525}
526
527template<typename T> void quad0_test()
528{
529 const char *spec = R"(4 0 0
530 0.0 1.0
531 1.0 0.0
532 2.0 0.1
533 2.25 0.5
534 )";
535
536 CDT_input<T> in = fill_input_from_string<T>(spec);
537 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
538 EXPECT_EQ(out.vert.size(), 4);
539 EXPECT_EQ(out.edge.size(), 5);
540 int e_diag_out = get_output_edge_index(out, 1, 3);
541 EXPECT_NE(e_diag_out, -1);
542 if (DO_DRAW) {
543 graph_draw<T>("Quad0", out.vert, out.edge, out.face);
544 }
545}
546
547template<typename T> void quad1_test()
548{
549 const char *spec = R"(4 0 0
550 0.0 0.0
551 0.9 -1.0
552 2.0 0.0
553 0.9 3.0
554 )";
555
556 CDT_input<T> in = fill_input_from_string<T>(spec);
557 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
558 EXPECT_EQ(out.vert.size(), 4);
559 EXPECT_EQ(out.edge.size(), 5);
560 int e_diag_out = get_output_edge_index(out, 0, 2);
561 EXPECT_NE(e_diag_out, -1);
562 if (DO_DRAW) {
563 graph_draw<T>("Quad1", out.vert, out.edge, out.face);
564 }
565}
566
567template<typename T> void quad2_test()
568{
569 const char *spec = R"(4 0 0
570 0.5 0.0
571 0.15 0.2
572 0.3 0.4
573 .45 0.35
574 )";
575
576 CDT_input<T> in = fill_input_from_string<T>(spec);
577 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
578 EXPECT_EQ(out.vert.size(), 4);
579 EXPECT_EQ(out.edge.size(), 5);
580 int e_diag_out = get_output_edge_index(out, 1, 3);
581 EXPECT_NE(e_diag_out, -1);
582 if (DO_DRAW) {
583 graph_draw<T>("Quad2", out.vert, out.edge, out.face);
584 }
585}
586
587template<typename T> void quad3_test()
588{
589 const char *spec = R"(4 0 0
590 0.5 0.0
591 0.0 0.0
592 0.3 0.4
593 .45 0.35
594 )";
595
596 CDT_input<T> in = fill_input_from_string<T>(spec);
597 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
598 EXPECT_EQ(out.vert.size(), 4);
599 EXPECT_EQ(out.edge.size(), 5);
600 int e_diag_out = get_output_edge_index(out, 0, 2);
601 EXPECT_NE(e_diag_out, -1);
602 if (DO_DRAW) {
603 graph_draw<T>("Quad3", out.vert, out.edge, out.face);
604 }
605}
606
607template<typename T> void quad4_test()
608{
609 const char *spec = R"(4 0 0
610 1.0 1.0
611 0.0 0.0
612 1.0 -3.0
613 0.0 1.0
614 )";
615
616 CDT_input<T> in = fill_input_from_string<T>(spec);
617 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
618 EXPECT_EQ(out.vert.size(), 4);
619 EXPECT_EQ(out.edge.size(), 5);
620 int e_diag_out = get_output_edge_index(out, 0, 1);
621 EXPECT_NE(e_diag_out, -1);
622 if (DO_DRAW) {
623 graph_draw<T>("Quad4", out.vert, out.edge, out.face);
624 }
625}
626
627template<typename T> void lineinsquare_test()
628{
629 const char *spec = R"(6 1 1
630 -0.5 -0.5
631 0.5 -0.5
632 -0.5 0.5
633 0.5 0.5
634 -0.25 0.0
635 0.25 0.0
636 4 5
637 0 1 3 2
638 )";
639
640 CDT_input<T> in = fill_input_from_string<T>(spec);
641 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
642 EXPECT_EQ(out.vert.size(), 6);
643 EXPECT_EQ(out.face.size(), 6);
644 if (DO_DRAW) {
645 graph_draw<T>("LineInSquare - full", out.vert, out.edge, out.face);
646 }
647 CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS);
648 EXPECT_EQ(out2.vert.size(), 6);
649 EXPECT_EQ(out2.face.size(), 1);
650 if (DO_DRAW) {
651 graph_draw<T>("LineInSquare - constraints", out2.vert, out2.edge, out2.face);
652 }
653 CDT_result<T> out3 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES);
654 EXPECT_EQ(out3.vert.size(), 6);
655 EXPECT_EQ(out3.face.size(), 6);
656 if (DO_DRAW) {
657 graph_draw<T>("LineInSquare - inside with holes", out3.vert, out3.edge, out3.face);
658 }
660 EXPECT_EQ(out4.vert.size(), 6);
661 EXPECT_EQ(out4.face.size(), 2);
662 if (DO_DRAW) {
663 graph_draw<T>("LineInSquare - valid bmesh with holes", out4.vert, out4.edge, out4.face);
664 }
665}
666
667template<typename T> void lineholeinsquare_test()
668{
669 const char *spec = R"(10 1 2
670 -0.5 -0.5
671 0.5 -0.5
672 -0.5 0.5
673 0.5 0.5
674 -0.25 0.0
675 0.25 0.0
676 -0.4 -0.4
677 0.4 -0.4
678 0.4 -0.3
679 -0.4 -0.3
680 4 5
681 0 1 3 2
682 6 7 8 9
683 )";
684
685 CDT_input<T> in = fill_input_from_string<T>(spec);
686 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
687 EXPECT_EQ(out.vert.size(), 10);
688 EXPECT_EQ(out.face.size(), 14);
689 if (DO_DRAW) {
690 graph_draw<T>("LineHoleInSquare - full", out.vert, out.edge, out.face);
691 }
692 CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS);
693 EXPECT_EQ(out2.vert.size(), 10);
694 EXPECT_EQ(out2.face.size(), 2);
695 if (DO_DRAW) {
696 graph_draw<T>("LineHoleInSquare - constraints", out2.vert, out2.edge, out2.face);
697 }
698 CDT_result<T> out3 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES);
699 EXPECT_EQ(out3.vert.size(), 10);
700 EXPECT_EQ(out3.face.size(), 12);
701 if (DO_DRAW) {
702 graph_draw<T>("LineHoleInSquare - inside with holes", out3.vert, out3.edge, out3.face);
703 }
705 EXPECT_EQ(out4.vert.size(), 10);
706 EXPECT_EQ(out4.face.size(), 2);
707 if (DO_DRAW) {
708 graph_draw<T>("LineHoleInSquare - valid bmesh with holes", out4.vert, out4.edge, out4.face);
709 }
710}
711
712template<typename T> void nestedholes_test()
713{
714 const char *spec = R"(12 0 3
715 -0.5 -0.5
716 0.5 -0.5
717 -0.5 0.5
718 0.5 0.5
719 -0.4 -0.4
720 0.4 -0.4
721 0.4 0.4
722 -0.4 0.4
723 -0.2 -0.2
724 0.2 -0.2
725 0.2 0.2
726 -0.2 0.2
727 0 1 3 2
728 4 7 6 5
729 8 9 10 11
730 )";
731
732 CDT_input<T> in = fill_input_from_string<T>(spec);
733 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
734 EXPECT_EQ(out.vert.size(), 12);
735 EXPECT_EQ(out.face.size(), 18);
736 if (DO_DRAW) {
737 graph_draw<T>("NestedHoles - full", out.vert, out.edge, out.face);
738 }
739 CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS);
740 EXPECT_EQ(out2.vert.size(), 12);
741 EXPECT_EQ(out2.face.size(), 3);
742 if (DO_DRAW) {
743 graph_draw<T>("NestedHoles - constraints", out2.vert, out2.edge, out2.face);
744 }
745 CDT_result<T> out3 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES);
746 EXPECT_EQ(out3.vert.size(), 12);
747 EXPECT_EQ(out3.face.size(), 10);
748 if (DO_DRAW) {
749 graph_draw<T>("NestedHoles - inside with holes", out3.vert, out3.edge, out3.face);
750 }
752 EXPECT_EQ(out4.vert.size(), 12);
753 EXPECT_EQ(out4.face.size(), 3);
754 if (DO_DRAW) {
755 graph_draw<T>("NestedHoles - valid bmesh with holes", out4.vert, out4.edge, out4.face);
756 }
757}
758
759template<typename T> void crosssegs_test()
760{
761 const char *spec = R"(4 2 0
762 -0.5 0.0
763 0.5 0.0
764 -0.4 -0.5
765 0.4 0.5
766 0 1
767 2 3
768 )";
769
770 CDT_input<T> in = fill_input_from_string<T>(spec);
771 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
772 EXPECT_EQ(out.vert.size(), 5);
773 EXPECT_EQ(out.edge.size(), 8);
774 EXPECT_EQ(out.face.size(), 4);
775 int v0_out = get_orig_index(out.vert_orig, 0);
776 int v1_out = get_orig_index(out.vert_orig, 1);
777 int v2_out = get_orig_index(out.vert_orig, 2);
778 int v3_out = get_orig_index(out.vert_orig, 3);
779 EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1);
780 if (out.vert.size() == 5) {
781 int v_intersect = -1;
782 for (int i = 0; i < 5; i++) {
783 if (!ELEM(i, v0_out, v1_out, v2_out, v3_out)) {
784 EXPECT_EQ(v_intersect, -1);
785 v_intersect = i;
786 }
787 }
788 EXPECT_NE(v_intersect, -1);
789 if (v_intersect != -1) {
790 expect_coord_near<T>(out.vert[v_intersect], VecBase<T, 2>(0, 0));
791 }
792 }
793 if (DO_DRAW) {
794 graph_draw<T>("CrossSegs", out.vert, out.edge, out.face);
795 }
796}
797
798template<typename T> void cutacrosstri_test()
799{
800 /* Right triangle with horizontal segment exactly crossing in the middle. */
801 const char *spec = R"(5 1 1
802 0.0 0.0
803 1.0 0.0
804 0.0 1.0
805 0.0 0.5
806 0.5 0.5
807 3 4
808 0 1 2
809 )";
810
811 CDT_input<T> in = fill_input_from_string<T>(spec);
812 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
813 EXPECT_EQ(out.vert.size(), 5);
814 EXPECT_EQ(out.edge.size(), 7);
815 EXPECT_EQ(out.face.size(), 3);
816 int v0_out = get_orig_index(out.vert_orig, 0);
817 int v1_out = get_orig_index(out.vert_orig, 1);
818 int v2_out = get_orig_index(out.vert_orig, 2);
819 int v3_out = get_orig_index(out.vert_orig, 3);
820 int v4_out = get_orig_index(out.vert_orig, 4);
821 EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1 && v4_out != -1);
822 if (out.face.size() == 3) {
823 int e0_out = get_orig_index(out.edge_orig, 0);
824 EXPECT_NE(e0_out, -1);
825 int fe0_out = get_output_edge_index(out, v0_out, v1_out);
826 EXPECT_NE(fe0_out, -1);
827 int fe1a_out = get_output_edge_index(out, v1_out, v4_out);
828 EXPECT_NE(fe1a_out, -1);
829 int fe1b_out = get_output_edge_index(out, v4_out, v2_out);
830 EXPECT_NE(fe1b_out, -1);
831 if (fe1a_out != 0 && fe1b_out != 0) {
832 EXPECT_EQ(e0_out, get_orig_index(out.edge_orig, 0));
833 EXPECT_TRUE(out.edge_orig[fe1a_out].size() == 1 && out.edge_orig[fe1a_out][0] == 11);
834 EXPECT_TRUE(out.edge_orig[fe1b_out].size() == 1 && out.edge_orig[fe1b_out][0] == 11);
835 }
836 int e_diag = get_output_edge_index(out, v0_out, v4_out);
837 EXPECT_NE(e_diag, -1);
838 if (e_diag != -1) {
839 EXPECT_EQ(out.edge_orig[e_diag].size(), 0);
840 }
841 }
842 if (DO_DRAW) {
843 graph_draw<T>("CutAcrossTri", out.vert, out.edge, out.face);
844 }
845}
846
847template<typename T> void diamondcross_test()
848{
849 /* Diamond with constraint edge from top to bottom. Some dup verts. */
850 const char *spec = R"(7 5 0
851 0.0 0.0
852 1.0 3.0
853 2.0 0.0
854 1.0 -3.0
855 0.0 0.0
856 1.0 -3.0
857 1.0 3.0
858 0 1
859 1 2
860 2 3
861 3 4
862 5 6
863 )";
864
865 CDT_input<T> in = fill_input_from_string<T>(spec);
866 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
867 EXPECT_EQ(out.vert.size(), 4);
868 EXPECT_EQ(out.edge.size(), 5);
869 EXPECT_EQ(out.face.size(), 2);
870 if (DO_DRAW) {
871 graph_draw<T>("DiamondCross", out.vert, out.edge, out.face);
872 }
873}
874
875template<typename T> void twodiamondscross_test()
876{
877 const char *spec = R"(12 9 0
878 0.0 0.0
879 1.0 2.0
880 2.0 0.0
881 1.0 -2.0
882 0.0 0.0
883 3.0 0.0
884 4.0 2.0
885 5.0 0.0
886 4.0 -2.0
887 3.0 0.0
888 0.0 0.0
889 5.0 0.0
890 0 1
891 1 2
892 2 3
893 3 4
894 5 6
895 6 7
896 7 8
897 8 9
898 10 11
899 )";
900
901 CDT_input<T> in = fill_input_from_string<T>(spec);
902 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
903 EXPECT_EQ(out.vert.size(), 8);
904 EXPECT_EQ(out.edge.size(), 15);
905 EXPECT_EQ(out.face.size(), 8);
906 if (out.vert.size() == 8 && out.edge.size() == 15 && out.face.size() == 8) {
907 int v_out[12];
908 for (int i = 0; i < 12; ++i) {
909 v_out[i] = get_orig_index(out.vert_orig, i);
910 EXPECT_NE(v_out[i], -1);
911 }
912 EXPECT_EQ(v_out[0], v_out[4]);
913 EXPECT_EQ(v_out[0], v_out[10]);
914 EXPECT_EQ(v_out[5], v_out[9]);
915 EXPECT_EQ(v_out[7], v_out[11]);
916 int e_out[9];
917 for (int i = 0; i < 8; ++i) {
918 e_out[i] = get_output_edge_index(out, v_out[in.edge[i].first], v_out[in.edge[i].second]);
919 EXPECT_NE(e_out[i], -1);
920 }
921 /* there won't be a single edge for the input cross edge, but rather 3 */
922 EXPECT_EQ(get_output_edge_index(out, v_out[10], v_out[11]), -1);
923 int e_cross_1 = get_output_edge_index(out, v_out[0], v_out[2]);
924 int e_cross_2 = get_output_edge_index(out, v_out[2], v_out[5]);
925 int e_cross_3 = get_output_edge_index(out, v_out[5], v_out[7]);
926 EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1);
927 EXPECT_TRUE(output_edge_has_input_id(out, e_cross_1, 8));
928 EXPECT_TRUE(output_edge_has_input_id(out, e_cross_2, 8));
929 EXPECT_TRUE(output_edge_has_input_id(out, e_cross_3, 8));
930 }
931 if (DO_DRAW) {
932 graph_draw<T>("TwoDiamondsCross", out.vert, out.edge, out.face);
933 }
934}
935
936template<typename T> void manycross_test()
937{
938 /* Input has some repetition of vertices, on purpose */
939 const char *spec = R"(27 21 0
940 0.0 0.0
941 6.0 9.0
942 15.0 18.0
943 35.0 13.0
944 43.0 18.0
945 57.0 12.0
946 69.0 10.0
947 78.0 0.0
948 91.0 0.0
949 107.0 22.0
950 123.0 0.0
951 0.0 0.0
952 10.0 -14.0
953 35.0 -8.0
954 43.0 -12.0
955 64.0 -13.0
956 78.0 0.0
957 91.0 0.0
958 102.0 -9.0
959 116.0 -9.0
960 123.0 0.0
961 43.0 18.0
962 43.0 -12.0
963 107.0 22.0
964 102.0 -9.0
965 0.0 0.0
966 123.0 0.0
967 0 1
968 1 2
969 2 3
970 3 4
971 4 5
972 5 6
973 6 7
974 7 8
975 8 9
976 9 10
977 11 12
978 12 13
979 13 14
980 14 15
981 15 16
982 17 18
983 18 19
984 19 20
985 21 22
986 23 24
987 25 26
988 )";
989
990 CDT_input<T> in = fill_input_from_string<T>(spec);
991 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
992 EXPECT_EQ(out.vert.size(), 19);
993 EXPECT_EQ(out.edge.size(), 46);
994 EXPECT_EQ(out.face.size(), 28);
995 if (DO_DRAW) {
996 graph_draw<T>("ManyCross", out.vert, out.edge, out.face);
997 }
998}
999
1000template<typename T> void twoface_test()
1001{
1002 const char *spec = R"(6 0 2
1003 0.0 0.0
1004 1.0 0.0
1005 0.5 1.0
1006 1.1 1.0
1007 1.1 0.0
1008 1.6 1.0
1009 0 1 2
1010 3 4 5
1011 )";
1012
1013 CDT_input<T> in = fill_input_from_string<T>(spec);
1014 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
1015 EXPECT_EQ(out.vert.size(), 6);
1016 EXPECT_EQ(out.edge.size(), 9);
1017 EXPECT_EQ(out.face.size(), 4);
1018 if (out.vert.size() == 6 && out.edge.size() == 9 && out.face.size() == 4) {
1019 int v_out[6];
1020 for (int i = 0; i < 6; i++) {
1021 v_out[i] = get_orig_index(out.vert_orig, i);
1022 EXPECT_NE(v_out[i], -1);
1023 }
1024 int f0_out = get_output_tri_index(out, v_out[0], v_out[1], v_out[2]);
1025 int f1_out = get_output_tri_index(out, v_out[3], v_out[4], v_out[5]);
1026 EXPECT_NE(f0_out, -1);
1027 EXPECT_NE(f1_out, -1);
1028 int e0_out = get_output_edge_index(out, v_out[0], v_out[1]);
1029 int e1_out = get_output_edge_index(out, v_out[1], v_out[2]);
1030 int e2_out = get_output_edge_index(out, v_out[2], v_out[0]);
1031 EXPECT_NE(e0_out, -1);
1032 EXPECT_NE(e1_out, -1);
1033 EXPECT_NE(e2_out, -1);
1034 EXPECT_TRUE(output_edge_has_input_id(out, e0_out, out.face_edge_offset + 0));
1035 EXPECT_TRUE(output_edge_has_input_id(out, e1_out, out.face_edge_offset + 1));
1036 EXPECT_TRUE(output_edge_has_input_id(out, e2_out, out.face_edge_offset + 2));
1037 EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0));
1038 EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1));
1039 }
1040 if (DO_DRAW) {
1041 graph_draw<T>("TwoFace", out.vert, out.edge, out.face);
1042 }
1043}
1044
1045template<typename T> void twoface2_test()
1046{
1047 const char *spec = R"(6 0 2
1048 0.0 0.0
1049 4.0 4.0
1050 -4.0 2.0
1051 3.0 0.0
1052 3.0 6.0
1053 -1.0 2.0
1054 0 1 2
1055 3 4 5
1056 )";
1057
1058 CDT_input<T> in = fill_input_from_string<T>(spec);
1059 CDT_result<T> out = delaunay_2d_calc(in, CDT_INSIDE);
1060 EXPECT_EQ(out.vert.size(), 10);
1061 EXPECT_EQ(out.edge.size(), 18);
1062 EXPECT_EQ(out.face.size(), 9);
1063 if (out.vert.size() == 10 && out.edge.size() == 18 && out.face.size() == 9) {
1064 /* Input verts have no duplicates, so expect output ones match input ones. */
1065 for (int i = 0; i < 6; i++) {
1066 EXPECT_EQ(get_orig_index(out.vert_orig, i), i);
1067 }
1068 int v6 = get_vertex_by_coord(out, 3.0, 3.0);
1069 EXPECT_NE(v6, -1);
1070 int v7 = get_vertex_by_coord(out, 3.0, 3.75);
1071 EXPECT_NE(v7, -1);
1072 int v8 = get_vertex_by_coord(out, 0.0, 3.0);
1073 EXPECT_NE(v8, -1);
1074 int v9 = get_vertex_by_coord(out, 1.0, 1.0);
1075 EXPECT_NE(v9, -1);
1076 /* f0 to f3 should be triangles part of input face 0, not part of input face 1. */
1077 int f0 = get_output_tri_index(out, 0, 9, 5);
1078 EXPECT_NE(f0, -1);
1079 EXPECT_TRUE(output_face_has_input_id(out, f0, 0));
1080 EXPECT_FALSE(output_face_has_input_id(out, f0, 1));
1081 int f1 = get_output_tri_index(out, 0, 5, 2);
1082 EXPECT_NE(f1, -1);
1083 EXPECT_TRUE(output_face_has_input_id(out, f1, 0));
1084 EXPECT_FALSE(output_face_has_input_id(out, f1, 1));
1085 int f2 = get_output_tri_index(out, 2, 5, 8);
1086 EXPECT_NE(f2, -1);
1087 EXPECT_TRUE(output_face_has_input_id(out, f2, 0));
1088 EXPECT_FALSE(output_face_has_input_id(out, f2, 1));
1089 int f3 = get_output_tri_index(out, 6, 1, 7);
1090 EXPECT_NE(f3, -1);
1091 EXPECT_TRUE(output_face_has_input_id(out, f3, 0));
1092 EXPECT_FALSE(output_face_has_input_id(out, f3, 1));
1093 /* f4 and f5 should be triangles part of input face 1, not part of input face 0. */
1094 int f4 = get_output_tri_index(out, 8, 7, 4);
1095 EXPECT_NE(f4, -1);
1096 EXPECT_FALSE(output_face_has_input_id(out, f4, 0));
1097 EXPECT_TRUE(output_face_has_input_id(out, f4, 1));
1098 int f5 = get_output_tri_index(out, 3, 6, 9);
1099 EXPECT_NE(f5, -1);
1100 EXPECT_FALSE(output_face_has_input_id(out, f5, 0));
1101 EXPECT_TRUE(output_face_has_input_id(out, f5, 1));
1102 /* f6 to f8 should be triangles part of both input faces. */
1103 int f6 = get_output_tri_index(out, 5, 9, 6);
1104 EXPECT_NE(f6, -1);
1105 EXPECT_TRUE(output_face_has_input_id(out, f6, 0));
1106 EXPECT_TRUE(output_face_has_input_id(out, f6, 1));
1107 int f7 = get_output_tri_index(out, 5, 6, 7);
1108 EXPECT_NE(f7, -1);
1109 EXPECT_TRUE(output_face_has_input_id(out, f7, 0));
1110 EXPECT_TRUE(output_face_has_input_id(out, f7, 1));
1111 int f8 = get_output_tri_index(out, 5, 7, 8);
1112 EXPECT_NE(f8, -1);
1113 EXPECT_TRUE(output_face_has_input_id(out, f8, 0));
1114 EXPECT_TRUE(output_face_has_input_id(out, f8, 1));
1115 }
1116 if (DO_DRAW) {
1117 graph_draw<T>("TwoFace2", out.vert, out.edge, out.face);
1118 }
1119}
1120
1121template<typename T> void overlapfaces_test()
1122{
1123 const char *spec = R"(12 0 3
1124 0.0 0.0
1125 1.0 0.0
1126 1.0 1.0
1127 0.0 1.0
1128 0.5 0.5
1129 1.5 0.5
1130 1.5 1.3
1131 0.5 1.3
1132 0.1 0.1
1133 0.3 0.1
1134 0.3 0.3
1135 0.1 0.3
1136 0 1 2 3
1137 4 5 6 7
1138 8 9 10 11
1139 )";
1140
1141 CDT_input<T> in = fill_input_from_string<T>(spec);
1142 CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL);
1143 EXPECT_EQ(out.vert.size(), 14);
1144 EXPECT_EQ(out.edge.size(), 33);
1145 EXPECT_EQ(out.face.size(), 20);
1146 if (out.vert.size() == 14 && out.edge.size() == 33 && out.face.size() == 20) {
1147 int v_out[12];
1148 for (int i = 0; i < 12; i++) {
1149 v_out[i] = get_orig_index(out.vert_orig, i);
1150 EXPECT_NE(v_out[i], -1);
1151 }
1152 int v_int1 = 12;
1153 int v_int2 = 13;
1154 T x = out.vert[v_int1][0] - T(1);
1155 if (math_abs(x) > in.epsilon) {
1156 v_int1 = 13;
1157 v_int2 = 12;
1158 }
1159 expect_coord_near<T>(out.vert[v_int1], VecBase<T, 2>(1, 0.5));
1160 expect_coord_near<T>(out.vert[v_int2], VecBase<T, 2>(0.5, 1));
1161 EXPECT_EQ(out.vert_orig[v_int1].size(), 0);
1162 EXPECT_EQ(out.vert_orig[v_int2].size(), 0);
1163 int f0_out = get_output_tri_index(out, v_out[1], v_int1, v_out[4]);
1164 EXPECT_NE(f0_out, -1);
1165 EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0));
1166 int f1_out = get_output_tri_index(out, v_out[4], v_int1, v_out[2]);
1167 EXPECT_NE(f1_out, -1);
1168 EXPECT_TRUE(output_face_has_input_id(out, f1_out, 0));
1169 EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1));
1170 int f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[10]);
1171 if (f2_out == -1) {
1172 f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[11]);
1173 }
1174 EXPECT_NE(f2_out, -1);
1175 EXPECT_TRUE(output_face_has_input_id(out, f2_out, 0));
1176 EXPECT_TRUE(output_face_has_input_id(out, f2_out, 2));
1177 }
1178 if (DO_DRAW) {
1179 graph_draw<T>("OverlapFaces - full", out.vert, out.edge, out.face);
1180 }
1181
1182 /* Different output types. */
1183 CDT_result<T> out2 = delaunay_2d_calc(in, CDT_INSIDE);
1184 EXPECT_EQ(out2.face.size(), 18);
1185 if (DO_DRAW) {
1186 graph_draw<T>("OverlapFaces - inside", out2.vert, out2.edge, out2.face);
1187 }
1188
1189 CDT_result<T> out3 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES);
1190 EXPECT_EQ(out3.face.size(), 14);
1191 if (DO_DRAW) {
1192 graph_draw<T>("OverlapFaces - inside with holes", out3.vert, out3.edge, out3.face);
1193 }
1194
1195 CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1196 EXPECT_EQ(out4.face.size(), 4);
1197 if (DO_DRAW) {
1198 graph_draw<T>("OverlapFaces - constraints", out4.vert, out4.edge, out4.face);
1199 }
1200
1201 CDT_result<T> out5 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
1202 EXPECT_EQ(out5.face.size(), 5);
1203 if (DO_DRAW) {
1204 graph_draw<T>("OverlapFaces - valid bmesh", out5.vert, out5.edge, out5.face);
1205 }
1206
1208 EXPECT_EQ(out6.face.size(), 3);
1209 if (DO_DRAW) {
1210 graph_draw<T>("OverlapFaces - valid bmesh with holes", out6.vert, out6.edge, out6.face);
1211 }
1212}
1213
1214template<typename T> void twosquaresoverlap_test()
1215{
1216 const char *spec = R"(8 0 2
1217 1.0 -1.0
1218 -1.0 -1.0
1219 -1.0 1.0
1220 1.0 1.0
1221 -1.5 1.5
1222 0.5 1.5
1223 0.5 -0.5
1224 -1.5 -0.5
1225 7 6 5 4
1226 3 2 1 0
1227 )";
1228
1229 CDT_input<T> in = fill_input_from_string<T>(spec);
1230 CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
1231 EXPECT_EQ(out.vert.size(), 10);
1232 EXPECT_EQ(out.edge.size(), 12);
1233 EXPECT_EQ(out.face.size(), 3);
1234 if (DO_DRAW) {
1235 graph_draw<T>("TwoSquaresOverlap", out.vert, out.edge, out.face);
1236 }
1237}
1238
1239template<typename T> void twofaceedgeoverlap_test()
1240{
1241 const char *spec = R"(6 0 2
1242 5.657 0.0
1243 -1.414 -5.831
1244 0.0 0.0
1245 5.657 0.0
1246 -2.121 -2.915
1247 0.0 0.0
1248 2 1 0
1249 5 4 3
1250 )";
1251
1252 CDT_input<T> in = fill_input_from_string<T>(spec);
1253 CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1254 EXPECT_EQ(out.vert.size(), 5);
1255 EXPECT_EQ(out.edge.size(), 7);
1256 EXPECT_EQ(out.face.size(), 3);
1257 if (out.vert.size() == 5 && out.edge.size() == 7 && out.face.size() == 3) {
1258 int v_int = 4;
1259 int v_out[6];
1260 for (int i = 0; i < 6; i++) {
1261 v_out[i] = get_orig_index(out.vert_orig, i);
1262 EXPECT_NE(v_out[i], -1);
1263 EXPECT_NE(v_out[i], v_int);
1264 }
1265 EXPECT_EQ(v_out[0], v_out[3]);
1266 EXPECT_EQ(v_out[2], v_out[5]);
1267 int e01 = get_output_edge_index(out, v_out[0], v_out[1]);
1268 int foff = out.face_edge_offset;
1269 EXPECT_TRUE(output_edge_has_input_id(out, e01, foff + 1));
1270 int e1i = get_output_edge_index(out, v_out[1], v_int);
1271 EXPECT_TRUE(output_edge_has_input_id(out, e1i, foff + 0));
1272 int ei2 = get_output_edge_index(out, v_int, v_out[2]);
1273 EXPECT_TRUE(output_edge_has_input_id(out, ei2, foff + 0));
1274 int e20 = get_output_edge_index(out, v_out[2], v_out[0]);
1275 EXPECT_TRUE(output_edge_has_input_id(out, e20, foff + 2));
1276 EXPECT_TRUE(output_edge_has_input_id(out, e20, 2 * foff + 2));
1277 int e24 = get_output_edge_index(out, v_out[2], v_out[4]);
1278 EXPECT_TRUE(output_edge_has_input_id(out, e24, 2 * foff + 0));
1279 int e4i = get_output_edge_index(out, v_out[4], v_int);
1280 EXPECT_TRUE(output_edge_has_input_id(out, e4i, 2 * foff + 1));
1281 int ei0 = get_output_edge_index(out, v_int, v_out[0]);
1282 EXPECT_TRUE(output_edge_has_input_id(out, ei0, 2 * foff + 1));
1283 int f02i = get_output_tri_index(out, v_out[0], v_out[2], v_int);
1284 EXPECT_NE(f02i, -1);
1285 EXPECT_TRUE(output_face_has_input_id(out, f02i, 0));
1286 EXPECT_TRUE(output_face_has_input_id(out, f02i, 1));
1287 int f24i = get_output_tri_index(out, v_out[2], v_out[4], v_int);
1288 EXPECT_NE(f24i, -1);
1289 EXPECT_TRUE(output_face_has_input_id(out, f24i, 1));
1290 EXPECT_FALSE(output_face_has_input_id(out, f24i, 0));
1291 int f10i = get_output_tri_index(out, v_out[1], v_out[0], v_int);
1292 EXPECT_NE(f10i, -1);
1293 EXPECT_TRUE(output_face_has_input_id(out, f10i, 0));
1294 EXPECT_FALSE(output_face_has_input_id(out, f10i, 1));
1295 }
1296 if (DO_DRAW) {
1297 graph_draw<T>("TwoFaceEdgeOverlap", out.vert, out.edge, out.face);
1298 }
1299}
1300
1301template<typename T> void triintri_test()
1302{
1303 const char *spec = R"(6 0 2
1304 -5.65685 0.0
1305 1.41421 -5.83095
1306 0.0 0.0
1307 -2.47487 -1.45774
1308 -0.707107 -2.91548
1309 -1.06066 -1.45774
1310 0 1 2
1311 3 4 5
1312 )";
1313
1314 CDT_input<T> in = fill_input_from_string<T>(spec);
1315 CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
1316 EXPECT_EQ(out.vert.size(), 6);
1317 EXPECT_EQ(out.edge.size(), 8);
1318 EXPECT_EQ(out.face.size(), 3);
1319 if (DO_DRAW) {
1320 graph_draw<T>("TriInTri", out.vert, out.edge, out.face);
1321 }
1322}
1323
1324template<typename T> void diamondinsquare_test()
1325{
1326 const char *spec = R"(8 0 2
1327 0.0 0.0
1328 1.0 0.0
1329 1.0 1.0
1330 0.0 1.0
1331 0.14644660940672627 0.5
1332 0.5 0.14644660940672627
1333 0.8535533905932737 0.5
1334 0.5 0.8535533905932737
1335 0 1 2 3
1336 4 5 6 7
1337 )";
1338
1339 CDT_input<T> in = fill_input_from_string<T>(spec);
1340 CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH);
1341 EXPECT_EQ(out.vert.size(), 8);
1342 EXPECT_EQ(out.edge.size(), 10);
1343 EXPECT_EQ(out.face.size(), 3);
1344 if (DO_DRAW) {
1345 graph_draw<T>("DiamondInSquare", out.vert, out.edge, out.face);
1346 }
1347}
1348
1349template<typename T> void diamondinsquarewire_test()
1350{
1351 const char *spec = R"(8 8 0
1352 0.0 0.0
1353 1.0 0.0
1354 1.0 1.0
1355 0.0 1.0
1356 0.14644660940672627 0.5
1357 0.5 0.14644660940672627
1358 0.8535533905932737 0.5
1359 0.5 0.8535533905932737
1360 0 1
1361 1 2
1362 2 3
1363 3 0
1364 4 5
1365 5 6
1366 6 7
1367 7 4
1368 )";
1369
1370 CDT_input<T> in = fill_input_from_string<T>(spec);
1371 CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1372 EXPECT_EQ(out.vert.size(), 8);
1373 EXPECT_EQ(out.edge.size(), 8);
1374 EXPECT_EQ(out.face.size(), 2);
1375 if (DO_DRAW) {
1376 graph_draw<T>("DiamondInSquareWire", out.vert, out.edge, out.face);
1377 }
1378}
1379
1380template<typename T> void repeatedge_test()
1381{
1382 const char *spec = R"(5 3 0
1383 0.0 0.0
1384 0.0 1.0
1385 1.0 1.1
1386 0.5 -0.5
1387 0.5 2.5
1388 0 1
1389 2 3
1390 2 3
1391 )";
1392
1393 CDT_input<T> in = fill_input_from_string<T>(spec);
1394 CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1395 EXPECT_EQ(out.edge.size(), 2);
1396 if (DO_DRAW) {
1397 graph_draw<T>("RepeatEdge", out.vert, out.edge, out.face);
1398 }
1399}
1400
1401template<typename T> void repeattri_test()
1402{
1403 const char *spec = R"(3 0 2
1404 0.0 0.0
1405 1.0 0.0
1406 0.5 1.0
1407 0 1 2
1408 0 1 2
1409 )";
1410
1411 CDT_input<T> in = fill_input_from_string<T>(spec);
1412 CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS);
1413 EXPECT_EQ(out.edge.size(), 3);
1414 EXPECT_EQ(out.face.size(), 1);
1415 EXPECT_TRUE(output_face_has_input_id(out, 0, 0));
1416 EXPECT_TRUE(output_face_has_input_id(out, 0, 1));
1417 if (DO_DRAW) {
1418 graph_draw<T>("RepeatTri", out.vert, out.edge, out.face);
1419 }
1420}
1421
1422template<typename T> void square_o_test()
1423{
1424 const char *spec = R"(8 0 2
1425 0.0 0.0
1426 1.0 0.0
1427 1.0 1.0
1428 0.0 1.0
1429 0.2 0.2
1430 0.2 0.8
1431 0.8 0.8
1432 0.8 0.2
1433 0 1 2 3
1434 4 5 6 7
1435 )";
1436 CDT_input<T> in = fill_input_from_string<T>(spec);
1437 CDT_result<T> out1 = delaunay_2d_calc(in, CDT_INSIDE_WITH_HOLES);
1438 EXPECT_EQ(out1.face.size(), 8);
1439 if (DO_DRAW) {
1440 graph_draw<T>("Square O - inside with holes", out1.vert, out1.edge, out1.face);
1441 }
1442
1444 EXPECT_EQ(out2.face.size(), 2);
1445 if (DO_DRAW) {
1446 graph_draw<T>("Square O - valid bmesh with holes", out2.vert, out2.edge, out2.face);
1447 }
1448}
1449
1450TEST(delaunay_d, Empty)
1451{
1453}
1454
1455TEST(delaunay_d, OnePt)
1456{
1458}
1459
1460TEST(delaunay_d, TwoPt)
1461{
1463}
1464
1465TEST(delaunay_d, ThreePt)
1466{
1468}
1469
1470TEST(delaunay_d, MixedPts)
1471{
1473}
1474
1475TEST(delaunay_d, Quad0)
1476{
1478}
1479
1480TEST(delaunay_d, Quad1)
1481{
1483}
1484
1485TEST(delaunay_d, Quad2)
1486{
1488}
1489
1490TEST(delaunay_d, Quad3)
1491{
1493}
1494
1495TEST(delaunay_d, Quad4)
1496{
1498}
1499
1500TEST(delaunay_d, LineInSquare)
1501{
1503}
1504
1505TEST(delaunay_d, LineHoleInSquare)
1506{
1508}
1509
1510TEST(delaunay_d, NestedHoles)
1511{
1513}
1514
1515TEST(delaunay_d, CrossSegs)
1516{
1518}
1519
1520TEST(delaunay_d, CutAcrossTri)
1521{
1523}
1524
1525TEST(delaunay_d, DiamondCross)
1526{
1528}
1529
1530TEST(delaunay_d, TwoDiamondsCross)
1531{
1533}
1534
1535TEST(delaunay_d, ManyCross)
1536{
1538}
1539
1540TEST(delaunay_d, TwoFace)
1541{
1543}
1544
1545TEST(delaunay_d, TwoFace2)
1546{
1548}
1549
1550TEST(delaunay_d, OverlapFaces)
1551{
1553}
1554
1555TEST(delaunay_d, TwoSquaresOverlap)
1556{
1558}
1559
1560TEST(delaunay_d, TwoFaceEdgeOverlap)
1561{
1563}
1564
1565TEST(delaunay_d, TriInTri)
1566{
1568}
1569
1570TEST(delaunay_d, DiamondInSquare)
1571{
1573}
1574
1575TEST(delaunay_d, DiamondInSquareWire)
1576{
1578}
1579
1580TEST(delaunay_d, RepeatEdge)
1581{
1583}
1584
1585TEST(delaunay_d, RepeatTri)
1586{
1588}
1589
1590TEST(delaunay_d, SquareO)
1591{
1593}
1594
1595# ifdef WITH_GMP
1596TEST(delaunay_m, Empty)
1597{
1599}
1600
1601TEST(delaunay_m, OnePt)
1602{
1604}
1605TEST(delaunay_m, TwoPt)
1606{
1608}
1609
1610TEST(delaunay_m, ThreePt)
1611{
1613}
1614
1615TEST(delaunay_m, MixedPts)
1616{
1618}
1619
1620TEST(delaunay_m, Quad0)
1621{
1623}
1624
1625TEST(delaunay_m, Quad1)
1626{
1628}
1629
1630TEST(delaunay_m, Quad2)
1631{
1633}
1634
1635TEST(delaunay_m, Quad3)
1636{
1638}
1639
1640TEST(delaunay_m, Quad4)
1641{
1643}
1644
1645TEST(delaunay_m, LineInSquare)
1646{
1648}
1649
1650TEST(delaunay_m, LineHoleInSquare)
1651{
1653}
1654
1655TEST(delaunay_m, NestedHoles)
1656{
1658}
1659
1660TEST(delaunay_m, CrossSegs)
1661{
1663}
1664
1665TEST(delaunay_m, CutAcrossTri)
1666{
1668}
1669
1670TEST(delaunay_m, DiamondCross)
1671{
1673}
1674
1675TEST(delaunay_m, TwoDiamondsCross)
1676{
1678}
1679
1680TEST(delaunay_m, ManyCross)
1681{
1683}
1684
1685TEST(delaunay_m, TwoFace)
1686{
1688}
1689
1690TEST(delaunay_m, TwoFace2)
1691{
1693}
1694
1695TEST(delaunay_m, OverlapFaces)
1696{
1698}
1699
1700TEST(delaunay_m, TwoSquaresOverlap)
1701{
1703}
1704
1705TEST(delaunay_m, TwoFaceEdgeOverlap)
1706{
1708}
1709
1710TEST(delaunay_m, TriInTri)
1711{
1713}
1714
1715TEST(delaunay_m, DiamondInSquare)
1716{
1718}
1719
1720TEST(delaunay_m, DiamondInSquareWire)
1721{
1723}
1724
1725TEST(delaunay_m, RepeatEdge)
1726{
1728}
1729
1730TEST(delaunay_m, RepeatTri)
1731{
1733}
1734# endif
1735#endif
1736
1737#if DO_TEXT_TESTS
1738template<typename T>
1739void text_test(
1740 int arc_points_num, int lets_per_line_num, int lines_num, CDT_output_type otype, bool need_ids)
1741{
1742 constexpr bool print_timing = true;
1743 /*
1744 * Make something like a letter B:
1745 *
1746 * 4------------3
1747 * | )
1748 * | 12--11 )
1749 * | | ) a3 ) a1
1750 * | 9---10 )
1751 * | )
1752 * | 2
1753 * | )
1754 * | 8----7 )
1755 * | | ) a2 ) a0
1756 * | 5----6 )
1757 * | )
1758 * 0------------1
1759 *
1760 * Where the numbers are the first 13 vertices, and the rest of
1761 * the vertices are in arcs a0, a1, a2, a3, each of which have
1762 * arc_points_num per arc in them.
1763 */
1764
1765 const char *b_before_arcs = R"(13 0 3
1766 0.0 0.0
1767 1.0 0.0
1768 1.0 1.5
1769 1.0 3.0
1770 0.0 3.0
1771 0.2 0.2
1772 0.6 0.2
1773 0.6 1.4
1774 0.2 1.4
1775 0.2 1.6
1776 0.6 1.6
1777 0.6 2.8
1778 0.2 2.8
1779 3 4 0 1 2
1780 6 5 8 7
1781 10 9 12 11
1782 )";
1783
1784 CDT_input<T> b_before_arcs_in = fill_input_from_string<T>(b_before_arcs);
1785 constexpr int narcs = 4;
1786 int b_npts = b_before_arcs_in.vert.size() + narcs * arc_points_num;
1787 constexpr int b_nfaces = 3;
1788 Array<VecBase<T, 2>> b_vert(b_npts);
1789 Array<Vector<int>> b_face(b_nfaces);
1790 std::copy(b_before_arcs_in.vert.begin(), b_before_arcs_in.vert.end(), b_vert.begin());
1791 std::copy(b_before_arcs_in.face.begin(), b_before_arcs_in.face.end(), b_face.begin());
1792 if (arc_points_num > 0) {
1793 b_face[0].pop_last(); /* We'll add center point back between arcs for outer face. */
1794 for (int arc = 0; arc < narcs; ++arc) {
1795 int arc_origin_vert;
1796 int arc_terminal_vert;
1797 bool ccw;
1798 switch (arc) {
1799 case 0:
1800 arc_origin_vert = 1;
1801 arc_terminal_vert = 2;
1802 ccw = true;
1803 break;
1804 case 1:
1805 arc_origin_vert = 2;
1806 arc_terminal_vert = 3;
1807 ccw = true;
1808 break;
1809 case 2:
1810 arc_origin_vert = 7;
1811 arc_terminal_vert = 6;
1812 ccw = false;
1813 break;
1814 case 3:
1815 arc_origin_vert = 11;
1816 arc_terminal_vert = 10;
1817 ccw = false;
1818 break;
1819 default:
1820 BLI_assert(false);
1821 }
1822 VecBase<T, 2> start_co = b_vert[arc_origin_vert];
1823 VecBase<T, 2> end_co = b_vert[arc_terminal_vert];
1824 VecBase<T, 2> center_co = 0.5 * (start_co + end_co);
1825 BLI_assert(start_co[0] == end_co[0]);
1826 double radius = abs(math_to_double<T>(end_co[1] - center_co[1]));
1827 double angle_delta = M_PI / (arc_points_num + 1);
1828 int start_vert = b_before_arcs_in.vert.size() + arc * arc_points_num;
1829 Vector<int> &face = b_face[(arc <= 1) ? 0 : arc - 1];
1830 for (int i = 0; i < arc_points_num; ++i) {
1831 VecBase<T, 2> delta;
1832 float ang = ccw ? (-M_PI_2 + (i + 1) * angle_delta) : (M_PI_2 - (i + 1) * angle_delta);
1833 delta[0] = T(radius * cos(ang));
1834 delta[1] = T(radius * sin(ang));
1835 b_vert[start_vert + i] = center_co + delta;
1836 face.append(start_vert + i);
1837 }
1838 if (arc == 0) {
1839 face.append(arc_terminal_vert);
1840 }
1841 }
1842 }
1843
1844 CDT_input<T> in;
1845 int tot_instances = lets_per_line_num * lines_num;
1846 if (tot_instances == 1) {
1847 in.vert = b_vert;
1848 in.face = b_face;
1849 }
1850 else {
1851 in.vert = Array<VecBase<T, 2>>(tot_instances * b_vert.size());
1852 in.face = Array<Vector<int>>(tot_instances * b_face.size());
1853 T cur_x = T(0);
1854 T cur_y = T(0);
1855 T delta_x = T(2);
1856 T delta_y = T(3.25);
1857 int instance = 0;
1858 for (int line = 0; line < lines_num; ++line) {
1859 for (int let = 0; let < lets_per_line_num; ++let) {
1860 VecBase<T, 2> co_offset(cur_x, cur_y);
1861 int in_v_offset = instance * b_vert.size();
1862 for (int v = 0; v < b_vert.size(); ++v) {
1863 in.vert[in_v_offset + v] = b_vert[v] + co_offset;
1864 }
1865 int in_f_offset = instance * b_face.size();
1866 for (int f : b_face.index_range()) {
1867 for (int fv : b_face[f]) {
1868 in.face[in_f_offset + f].append(in_v_offset + fv);
1869 }
1870 }
1871 cur_x += delta_x;
1872 ++instance;
1873 }
1874 cur_y += delta_y;
1875 cur_x = T(0);
1876 }
1877 }
1878 in.epsilon = b_before_arcs_in.epsilon;
1879 in.need_ids = need_ids;
1880 double tstart = BLI_time_now_seconds();
1881 CDT_result<T> out = delaunay_2d_calc(in, otype);
1882 double tend = BLI_time_now_seconds();
1883 if (print_timing) {
1884 std::cout << "time = " << tend - tstart << "\n";
1885 }
1886 if (!need_ids) {
1887 EXPECT_EQ(out.vert_orig.size(), 0);
1888 EXPECT_EQ(out.edge_orig.size(), 0);
1889 EXPECT_EQ(out.face_orig.size(), 0);
1890 }
1891 if (DO_DRAW) {
1892 std::string label = "Text arcpts=" + std::to_string(arc_points_num);
1893 if (lets_per_line_num > 1) {
1894 label += " linelen=" + std::to_string(lets_per_line_num);
1895 }
1896 if (lines_num > 1) {
1897 label += " lines=" + std::to_string(lines_num);
1898 }
1899 if (!need_ids) {
1900 label += " no_ids";
1901 }
1902 if (otype != CDT_INSIDE_WITH_HOLES) {
1903 label += " otype=" + std::to_string(otype);
1904 }
1905 graph_draw<T>(label, out.vert, out.edge, out.face);
1906 }
1907}
1908
1909TEST(delaunay_d, TextB10)
1910{
1911 text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES, true);
1912}
1913
1914TEST(delaunay_d, TextB10_noids)
1915{
1916 text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES, false);
1917}
1918
1919TEST(delaunay_d, TextB10_inside)
1920{
1921 text_test<double>(10, 1, 1, CDT_INSIDE, true);
1922}
1923
1924TEST(delaunay_d, TextB10_inside_noids)
1925{
1926 text_test<double>(10, 1, 1, CDT_INSIDE, false);
1927}
1928
1929TEST(delaunay_d, TextB10_constraints)
1930{
1931 text_test<double>(10, 1, 1, CDT_CONSTRAINTS, true);
1932}
1933
1934TEST(delaunay_d, TextB10_constraints_noids)
1935{
1936 text_test<double>(10, 1, 1, CDT_CONSTRAINTS, false);
1937}
1938
1939TEST(delaunay_d, TextB10_constraints_valid_bmesh)
1940{
1941 text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH, true);
1942}
1943
1944TEST(delaunay_d, TextB10_constraints_valid_bmesh_noids)
1945{
1946 text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH, false);
1947}
1948
1949TEST(delaunay_d, TextB10_constraints_valid_bmesh_with_holes)
1950{
1951 text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES, true);
1952}
1953
1954TEST(delaunay_d, TextB10_constraints_valid_bmesh_with_holes_noids)
1955{
1956 text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES, false);
1957}
1958
1959TEST(delaunay_d, TextB200)
1960{
1961 text_test<double>(200, 1, 1, CDT_INSIDE_WITH_HOLES, true);
1962}
1963
1964TEST(delaunay_d, TextB10_10_10)
1965{
1966 text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES, true);
1967}
1968
1969TEST(delaunay_d, TextB10_10_10_noids)
1970{
1971 text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES, false);
1972}
1973
1974# ifdef WITH_GMP
1975TEST(delaunay_m, TextB10)
1976{
1977 text_test<mpq_class>(10, 1, 1, CDT_INSIDE_WITH_HOLES, true);
1978}
1979
1980TEST(delaunay_m, TextB200)
1981{
1982 text_test<mpq_class>(200, 1, 1, CDT_INSIDE_WITH_HOLES, true);
1983}
1984
1985TEST(delaunay_m, TextB10_10_10)
1986{
1987 text_test<mpq_class>(10, 10, 10, CDT_INSIDE_WITH_HOLES, true);
1988}
1989
1990TEST(delaunay_m, TextB10_10_10_noids)
1991{
1992 text_test<mpq_class>(10, 10, 10, CDT_INSIDE_WITH_HOLES, false);
1993}
1994# endif
1995
1996#endif
1997
1998#if DO_RANDOM_TESTS
1999
2000enum {
2001 RANDOM_PTS,
2002 RANDOM_SEGS,
2003 RANDOM_POLY,
2004 RANDOM_TILTED_GRID,
2005 RANDOM_CIRCLE,
2006 RANDOM_TRI_BETWEEN_CIRCLES,
2007};
2008
2009template<typename T>
2010void rand_delaunay_test(int test_kind,
2011 int start_lg_size,
2012 int max_lg_size,
2013 int reps_per_size,
2014 double param,
2015 CDT_output_type otype)
2016{
2017 constexpr bool print_timing = true;
2018 RNG *rng = BLI_rng_new(0);
2019 Array<double> times(max_lg_size + 1);
2020
2021 /* For powers of 2 sizes up to max_lg_size power of 2. */
2022 for (int lg_size = start_lg_size; lg_size <= max_lg_size; ++lg_size) {
2023 int size = 1 << lg_size;
2024 times[lg_size] = 0.0;
2025 if (size == 1 && test_kind != RANDOM_PTS) {
2026 continue;
2027 }
2028 /* Do 'rep' repetitions. */
2029 for (int rep = 0; rep < reps_per_size; ++rep) {
2030 /* First use test type and size to set npts, nedges, and nfaces. */
2031 int npts = 0;
2032 int nedges = 0;
2033 int nfaces = 0;
2034 std::string test_label;
2035 switch (test_kind) {
2036 case RANDOM_PTS: {
2037 npts = size;
2038 test_label = std::to_string(npts) + "Random points";
2039 break;
2040 }
2041 case RANDOM_SEGS: {
2042 npts = size;
2043 nedges = npts - 1;
2044 test_label = std::to_string(nedges) + "Random edges";
2045 break;
2046 }
2047 case RANDOM_POLY: {
2048 npts = size;
2049 nedges = npts;
2050 test_label = "Random poly with " + std::to_string(nedges) + " edges";
2051 break;
2052 }
2053 case RANDOM_TILTED_GRID: {
2054 /* A 'size' x 'size' grid of points, tilted by angle 'param'.
2055 * Edges will go from left ends to right ends and tops to bottoms,
2056 * so 2 x size of them.
2057 * Depending on epsilon, the vertical-ish edges may or may not go
2058 * through the intermediate vertices, but the horizontal ones always should.
2059 * 'param' is slope of tilt of vertical lines.
2060 */
2061 npts = size * size;
2062 nedges = 2 * size;
2063 test_label = "Tilted grid " + std::to_string(npts) + "x" + std::to_string(npts) +
2064 " (tilt=" + std::to_string(param) + ")";
2065 break;
2066 }
2067 case RANDOM_CIRCLE: {
2068 /* A circle with 'size' points, a random start angle,
2069 * and equal spacing thereafter. Will be input as one face.
2070 */
2071 npts = size;
2072 nfaces = 1;
2073 test_label = "Circle with " + std::to_string(npts) + " points";
2074 break;
2075 }
2076 case RANDOM_TRI_BETWEEN_CIRCLES: {
2077 /* A set of 'size' triangles, each has two random points on the unit circle,
2078 * and the third point is a random point on the circle with radius 'param'.
2079 * Each triangle will be input as a face.
2080 */
2081 npts = 3 * size;
2082 nfaces = size;
2083 test_label = "Random " + std::to_string(nfaces) +
2084 " triangles between circles (inner radius=" + std::to_string(param) + ")";
2085 break;
2086 }
2087 default:
2088 std::cout << "unknown delaunay test type\n";
2089 BLI_rng_free(rng);
2090 return;
2091 }
2092 if (otype != CDT_FULL) {
2093 if (otype == CDT_INSIDE) {
2094 test_label += " (inside)";
2095 }
2096 else if (otype == CDT_CONSTRAINTS) {
2097 test_label += " (constraints)";
2098 }
2099 else if (otype == CDT_CONSTRAINTS_VALID_BMESH) {
2100 test_label += " (valid bmesh)";
2101 }
2102 }
2103
2104 CDT_input<T> in;
2105 in.vert = Array<VecBase<T, 2>>(npts);
2106 if (nedges > 0) {
2107 in.edge = Array<std::pair<int, int>>(nedges);
2108 }
2109 if (nfaces > 0) {
2110 in.face = Array<Vector<int>>(nfaces);
2111 }
2112
2113 /* Make vertices and edges or faces. */
2114 switch (test_kind) {
2115 case RANDOM_PTS:
2116 case RANDOM_SEGS:
2117 case RANDOM_POLY: {
2118 for (int i = 0; i < size; i++) {
2119 in.vert[i][0] = T(BLI_rng_get_double(rng)); /* will be in range in [0,1) */
2120 in.vert[i][1] = T(BLI_rng_get_double(rng));
2121 if (test_kind != RANDOM_PTS) {
2122 if (i > 0) {
2123 in.edge[i - 1].first = i - 1;
2124 in.edge[i - 1].second = i;
2125 }
2126 }
2127 }
2128 if (test_kind == RANDOM_POLY) {
2129 in.edge[size - 1].first = size - 1;
2130 in.edge[size - 1].second = 0;
2131 }
2132 break;
2133 }
2134 case RANDOM_TILTED_GRID: {
2135 for (int i = 0; i < size; ++i) {
2136 for (int j = 0; j < size; ++j) {
2137 in.vert[i * size + j][0] = T(i * param + j);
2138 in.vert[i * size + j][1] = T(i);
2139 }
2140 }
2141 for (int i = 0; i < size; ++i) {
2142 /* Horizontal edges: connect `p(i,0)` to `p(i,size-1)`. */
2143 in.edge[i].first = i * size;
2144 in.edge[i].second = i * size + size - 1;
2145 /* Vertical edges: connect `p(0,i)` to `p(size-1,i)`. */
2146 in.edge[size + i].first = i;
2147 in.edge[size + i].second = (size - 1) * size + i;
2148 }
2149 break;
2150 }
2151 case RANDOM_CIRCLE: {
2152 double start_angle = BLI_rng_get_double(rng) * 2.0 * M_PI;
2153 double angle_delta = 2.0 * M_PI / size;
2154 for (int i = 0; i < size; i++) {
2155 in.vert[i][0] = T(cos(start_angle + i * angle_delta));
2156 in.vert[i][1] = T(sin(start_angle + i * angle_delta));
2157 in.face[0].append(i);
2158 }
2159 break;
2160 }
2161 case RANDOM_TRI_BETWEEN_CIRCLES: {
2162 for (int i = 0; i < size; i++) {
2163 /* Get three random angles in [0, 2pi). */
2164 double angle1 = BLI_rng_get_double(rng) * 2.0 * M_PI;
2165 double angle2 = BLI_rng_get_double(rng) * 2.0 * M_PI;
2166 double angle3 = BLI_rng_get_double(rng) * 2.0 * M_PI;
2167 int ia = 3 * i;
2168 int ib = 3 * i + 1;
2169 int ic = 3 * i + 2;
2170 in.vert[ia][0] = T(cos(angle1));
2171 in.vert[ia][1] = T(sin(angle1));
2172 in.vert[ib][0] = T(cos(angle2));
2173 in.vert[ib][1] = T(sin(angle2));
2174 in.vert[ic][0] = T((param * cos(angle3)));
2175 in.vert[ic][1] = T((param * sin(angle3)));
2176 /* Put the coordinates in CCW order. */
2177 in.face[i].append(ia);
2178 int orient = orient2d(in.vert[ia], in.vert[ib], in.vert[ic]);
2179 if (orient >= 0) {
2180 in.face[i].append(ib);
2181 in.face[i].append(ic);
2182 }
2183 else {
2184 in.face[i].append(ic);
2185 in.face[i].append(ib);
2186 }
2187 }
2188 break;
2189 }
2190 }
2191
2192 /* Run the test. */
2193 double tstart = BLI_time_now_seconds();
2194 CDT_result<T> out = delaunay_2d_calc(in, otype);
2195 EXPECT_NE(out.vert.size(), 0);
2196 times[lg_size] += BLI_time_now_seconds() - tstart;
2197 if (DO_DRAW) {
2198 graph_draw<T>(test_label, out.vert, out.edge, out.face);
2199 }
2200 }
2201 }
2202 if (print_timing) {
2203 std::cout << "\nsize,time\n";
2204 for (int lg_size = 0; lg_size <= max_lg_size; lg_size++) {
2205 int size = 1 << lg_size;
2206 std::cout << size << "," << times[lg_size] << "\n";
2207 }
2208 }
2209 BLI_rng_free(rng);
2210}
2211
2212TEST(delaunay_d, RandomPts)
2213{
2214 rand_delaunay_test<double>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL);
2215}
2216
2217TEST(delaunay_d, RandomSegs)
2218{
2219 rand_delaunay_test<double>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL);
2220}
2221
2222TEST(delaunay_d, RandomPoly)
2223{
2224 rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL);
2225}
2226
2227TEST(delaunay_d, RandomPolyConstraints)
2228{
2229 rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS);
2230}
2231
2232TEST(delaunay_d, RandomPolyValidBmesh)
2233{
2234 rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH);
2235}
2236
2237TEST(delaunay_d, Grid)
2238{
2239 rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL);
2240}
2241
2242TEST(delaunay_d, TiltedGridA)
2243{
2244 rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL);
2245}
2246
2247TEST(delaunay_d, TiltedGridB)
2248{
2249 rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL);
2250}
2251
2252TEST(delaunay_d, RandomCircle)
2253{
2254 rand_delaunay_test<double>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL);
2255}
2256
2257TEST(delaunay_d, RandomTrisCircle)
2258{
2259 rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL);
2260}
2261
2262TEST(delaunay_d, RandomTrisCircleB)
2263{
2264 rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL);
2265}
2266
2267# ifdef WITH_GMP
2268TEST(delaunay_m, RandomPts)
2269{
2270 rand_delaunay_test<mpq_class>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL);
2271}
2272
2273TEST(delaunay_m, RandomSegs)
2274{
2275 rand_delaunay_test<mpq_class>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL);
2276}
2277
2278TEST(delaunay_m, RandomPoly)
2279{
2280 rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL);
2281}
2282
2283TEST(delaunay_d, RandomPolyInside)
2284{
2285 rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE);
2286}
2287
2288TEST(delaunay_m, RandomPolyInside)
2289{
2290 rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE);
2291}
2292
2293TEST(delaunay_m, RandomPolyConstraints)
2294{
2295 rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS);
2296}
2297
2298TEST(delaunay_m, RandomPolyValidBmesh)
2299{
2300 rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH);
2301}
2302
2303TEST(delaunay_m, Grid)
2304{
2305 rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL);
2306}
2307
2308TEST(delaunay_m, TiltedGridA)
2309{
2310 rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL);
2311}
2312
2313TEST(delaunay_m, TiltedGridB)
2314{
2315 rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL);
2316}
2317
2318TEST(delaunay_m, RandomCircle)
2319{
2320 rand_delaunay_test<mpq_class>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL);
2321}
2322
2323TEST(delaunay_m, RandomTrisCircle)
2324{
2325 rand_delaunay_test<mpq_class>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL);
2326}
2327
2328TEST(delaunay_m, RandomTrisCircleB)
2329{
2330 rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL);
2331}
2332# endif
2333
2334#endif
2335
2336} // namespace blender::meshintersect
#define BLI_assert(a)
Definition BLI_assert.h:50
CDT_output_type
@ CDT_CONSTRAINTS_VALID_BMESH
@ CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES
@ CDT_CONSTRAINTS
@ CDT_INSIDE
@ CDT_FULL
@ CDT_INSIDE_WITH_HOLES
void expect_coord_near< double >(const double2 &testco, const double2 &refco)
void diamondcross_test()
void twodiamondscross_test()
void overlapfaces_test()
void repeattri_test()
void triintri_test()
void empty_test()
void twoface_test()
void crosssegs_test()
void onept_test()
void lineinsquare_test()
void quad2_test()
void quad3_test()
void diamondinsquarewire_test()
void twofaceedgeoverlap_test()
void twopt_test()
constexpr bool DO_DRAW
void nestedholes_test()
void quad0_test()
void cutacrosstri_test()
void square_o_test()
void twosquaresoverlap_test()
void repeatedge_test()
void mixedpts_test()
void quad1_test()
void threept_test()
void manycross_test()
void expect_coord_near(const VecBase< T, 2 > &testco, const VecBase< T, 2 > &refco)
void quad4_test()
void lineholeinsquare_test()
void twoface2_test()
void diamondinsquare_test()
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define M_PI_2
#define M_PI
Math vector functions needed specifically for mesh intersect and boolean.
Random number functions.
struct RNG * BLI_rng_new(unsigned int seed)
Definition rand.cc:39
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition rand.cc:58
double BLI_rng_get_double(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition rand.cc:88
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define ELEM(...)
Read Guarded memory(de)allocation.
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object instance
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
int64_t size() const
Definition BLI_array.hh:245
IndexRange index_range() const
Definition BLI_array.hh:349
const T & first() const
Definition BLI_array.hh:270
Array< std::pair< int, int > > edge
Array< std::pair< int, int > > edge
const char * label
#define SY(y)
#define SX(x)
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
ccl_device_inline float2 fabs(const float2 a)
#define T
static char faces[256]
T cos(const AngleRadianBase< T > &a)
std::ostream & operator<<(std::ostream &stream, EulerOrder order)
T sin(const AngleRadianBase< T > &a)
T abs(const T &a)
double math_to_double< double >(const double v)
int get_output_edge_index(const CDT_result< T > &out, int out_index_1, int out_index_2)
CDT_input< T > fill_input_from_string(const char *spec)
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)
CDT_result< double > delaunay_2d_calc(const CDT_input< double > &input, CDT_output_type output_type)
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)
void graph_draw(const std::string &label, const Span< VecBase< T, 2 > > verts, const Span< std::pair< int, int > > edges, const Span< Vector< int > > faces)
double math_to_double(const T)
int get_output_face_index(const CDT_result< T > &out, const Array< int > &poly)
static int get_orig_index(const Span< Vector< int > > out_to_orig, int orig_index)
TEST(BLI_string_utils, BLI_uniquename_cb)
int orient2d(const double2 &a, const double2 &b, const double2 &c)
VecBase< double, 2 > double2
Definition rand.cc:33