1 /* Apache License, Version 2.0 */
2 
3 #include "testing/testing.h"
4 
5 #include <algorithm>
6 #include <fstream>
7 #include <iostream>
8 
9 #include "PIL_time.h"
10 
11 #include "BLI_array.hh"
12 #include "BLI_math_mpq.hh"
13 #include "BLI_mesh_intersect.hh"
14 #include "BLI_mpq3.hh"
15 #include "BLI_task.h"
16 #include "BLI_vector.hh"
17 
18 #define DO_REGULAR_TESTS 1
19 #define DO_PERF_TESTS 0
20 
21 #ifdef WITH_GMP
22 namespace blender::meshintersect::tests {
23 
24 constexpr bool DO_OBJ = false;
25 
26 /* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */
27 class IMeshBuilder {
28  public:
29   IMesh imesh;
30   IMeshArena arena;
31 
32   /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */
33   static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */
34 
edge_index(int face_index,int facepos)35   static int edge_index(int face_index, int facepos)
36   {
37     return face_index * MAX_FACE_LEN + facepos;
38   }
39 
face_and_pos_for_edge_index(int e_index)40   static std::pair<int, int> face_and_pos_for_edge_index(int e_index)
41   {
42     return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN);
43   }
44 
45   /*
46    * Spec should have form:
47    *  #verts #faces
48    *  mpq_class mpq_class mpq_clas [#verts lines]
49    *  int int int ... [#faces lines; indices into verts for given face]
50    */
IMeshBuilder(const char * spec)51   IMeshBuilder(const char *spec)
52   {
53     std::istringstream ss(spec);
54     std::string line;
55     getline(ss, line);
56     std::istringstream hdrss(line);
57     int nv, nf;
58     hdrss >> nv >> nf;
59     if (nv == 0 || nf == 0) {
60       return;
61     }
62     arena.reserve(nv, nf);
63     Vector<const Vert *> verts;
64     Vector<Face *> faces;
65     bool spec_ok = true;
66     int v_index = 0;
67     while (v_index < nv && spec_ok && getline(ss, line)) {
68       std::istringstream iss(line);
69       mpq_class p0;
70       mpq_class p1;
71       mpq_class p2;
72       iss >> p0 >> p1 >> p2;
73       spec_ok = !iss.fail();
74       if (spec_ok) {
75         verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index));
76       }
77       ++v_index;
78     }
79     if (v_index != nv) {
80       spec_ok = false;
81     }
82     int f_index = 0;
83     while (f_index < nf && spec_ok && getline(ss, line)) {
84       std::istringstream fss(line);
85       Vector<const Vert *> face_verts;
86       Vector<int> edge_orig;
87       int fpos = 0;
88       while (spec_ok && fss >> v_index) {
89         if (v_index < 0 || v_index >= nv) {
90           spec_ok = false;
91           continue;
92         }
93         face_verts.append(verts[v_index]);
94         edge_orig.append(edge_index(f_index, fpos));
95         ++fpos;
96       }
97       if (fpos < 3) {
98         spec_ok = false;
99       }
100       if (spec_ok) {
101         Face *facep = arena.add_face(face_verts, f_index, edge_orig);
102         faces.append(facep);
103       }
104       ++f_index;
105     }
106     if (f_index != nf) {
107       spec_ok = false;
108     }
109     if (!spec_ok) {
110       std::cout << "Bad spec: " << spec;
111       return;
112     }
113     imesh = IMesh(faces);
114   }
115 };
116 
117 /* Return a const Face * in mesh with verts equal to v0, v1, and v2, in
118  * some cyclic order; return nullptr if not found.
119  */
find_tri_with_verts(const IMesh & mesh,const Vert * v0,const Vert * v1,const Vert * v2)120 static const Face *find_tri_with_verts(const IMesh &mesh,
121                                        const Vert *v0,
122                                        const Vert *v1,
123                                        const Vert *v2)
124 {
125   Face f_arg({v0, v1, v2}, 0, NO_INDEX);
126   for (const Face *f : mesh.faces()) {
127     if (f->cyclic_equal(f_arg)) {
128       return f;
129     }
130   }
131   return nullptr;
132 }
133 
134 /* How many instances of a triangle with v0, v1, v2 are in the mesh? */
count_tris_with_verts(const IMesh & mesh,const Vert * v0,const Vert * v1,const Vert * v2)135 static int count_tris_with_verts(const IMesh &mesh, const Vert *v0, const Vert *v1, const Vert *v2)
136 {
137   Face f_arg({v0, v1, v2}, 0, NO_INDEX);
138   int ans = 0;
139   for (const Face *f : mesh.faces()) {
140     if (f->cyclic_equal(f_arg)) {
141       ++ans;
142     }
143   }
144   return ans;
145 }
146 
147 /* What is the starting position, if any, of the edge (v0, v1), in either order, in f? -1 if none.
148  */
find_edge_pos_in_tri(const Vert * v0,const Vert * v1,const Face * f)149 static int find_edge_pos_in_tri(const Vert *v0, const Vert *v1, const Face *f)
150 {
151   for (int pos : f->index_range()) {
152     int nextpos = f->next_pos(pos);
153     if (((*f)[pos] == v0 && (*f)[nextpos] == v1) || ((*f)[pos] == v1 && (*f)[nextpos] == v0)) {
154       return static_cast<int>(pos);
155     }
156   }
157   return -1;
158 }
159 
160 #  if DO_REGULAR_TESTS
TEST(mesh_intersect,Mesh)161 TEST(mesh_intersect, Mesh)
162 {
163   Vector<const Vert *> verts;
164   Vector<Face *> faces;
165   IMeshArena arena;
166 
167   verts.append(arena.add_or_find_vert(mpq3(0, 0, 1), 0));
168   verts.append(arena.add_or_find_vert(mpq3(1, 0, 1), 1));
169   verts.append(arena.add_or_find_vert(mpq3(0.5, 1, 1), 2));
170   faces.append(arena.add_face(verts, 0, {10, 11, 12}));
171 
172   IMesh mesh(faces);
173   const Face *f = mesh.face(0);
174   EXPECT_TRUE(f->is_tri());
175 }
176 
TEST(mesh_intersect,OneTri)177 TEST(mesh_intersect, OneTri)
178 {
179   const char *spec = R"(3 1
180   0 0 0
181   1 0 0
182   1/2 1 0
183   0 1 2
184   )";
185 
186   IMeshBuilder mb(spec);
187   IMesh imesh = trimesh_self_intersect(mb.imesh, &mb.arena);
188   imesh.populate_vert();
189   EXPECT_EQ(imesh.vert_size(), 3);
190   EXPECT_EQ(imesh.face_size(), 1);
191   const Face &f_in = *mb.imesh.face(0);
192   const Face &f_out = *imesh.face(0);
193   EXPECT_EQ(f_in.orig, f_out.orig);
194   for (int i = 0; i < 3; ++i) {
195     EXPECT_EQ(f_in[i], f_out[i]);
196     EXPECT_EQ(f_in.edge_orig[i], f_out.edge_orig[i]);
197   }
198 }
199 
TEST(mesh_intersect,TriTri)200 TEST(mesh_intersect, TriTri)
201 {
202   const char *spec = R"(6 2
203   0 0 0
204   4 0 0
205   0 4 0
206   1 0 0
207   2 0 0
208   1 1 0
209   0 1 2
210   3 4 5
211   )";
212 
213   /* Second triangle is smaller and congruent to first, resting on same base, partway along. */
214   IMeshBuilder mb(spec);
215   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
216   out.populate_vert();
217   EXPECT_EQ(out.vert_size(), 6);
218   EXPECT_EQ(out.face_size(), 6);
219   if (out.vert_size() == 6 && out.face_size() == 6) {
220     const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0));
221     const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0));
222     const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0));
223     const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0));
224     const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0));
225     const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0));
226     EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr);
227     EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr);
228     if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr &&
229         v5 != nullptr) {
230       EXPECT_EQ(v0->orig, 0);
231       EXPECT_EQ(v1->orig, 1);
232       const Face *f0 = find_tri_with_verts(out, v4, v1, v5);
233       const Face *f1 = find_tri_with_verts(out, v3, v4, v5);
234       const Face *f2 = find_tri_with_verts(out, v0, v3, v5);
235       const Face *f3 = find_tri_with_verts(out, v0, v5, v2);
236       const Face *f4 = find_tri_with_verts(out, v5, v1, v2);
237       EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr &&
238                   f4 != nullptr);
239       /* For boolean to work right, there need to be two copies of the smaller triangle in the
240        * output. */
241       EXPECT_EQ(count_tris_with_verts(out, v3, v4, v5), 2);
242       if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) {
243         EXPECT_EQ(f0->orig, 0);
244         EXPECT_TRUE(f1->orig == 0 || f1->orig == 1);
245         EXPECT_EQ(f2->orig, 0);
246         EXPECT_EQ(f3->orig, 0);
247         EXPECT_EQ(f4->orig, 0);
248       }
249       int e03 = find_edge_pos_in_tri(v0, v3, f2);
250       int e34 = find_edge_pos_in_tri(v3, v4, f1);
251       int e45 = find_edge_pos_in_tri(v4, v5, f1);
252       int e05 = find_edge_pos_in_tri(v0, v5, f3);
253       int e15 = find_edge_pos_in_tri(v1, v5, f0);
254       EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1);
255       if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) {
256         EXPECT_EQ(f2->edge_orig[e03], 0);
257         EXPECT_TRUE(f1->edge_orig[e34] == 0 ||
258                     f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN);
259         EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1);
260         EXPECT_EQ(f3->edge_orig[e05], NO_INDEX);
261         EXPECT_EQ(f0->edge_orig[e15], NO_INDEX);
262       }
263     }
264   }
265   if (DO_OBJ) {
266     write_obj_mesh(out, "tritri");
267   }
268 }
269 
TEST(mesh_intersect,TriTriReversed)270 TEST(mesh_intersect, TriTriReversed)
271 {
272   /* Like TriTri but with triangles of opposite orientation.
273    * This matters because projection to 2D will now need reversed triangles. */
274   const char *spec = R"(6 2
275   0 0 0
276   4 0 0
277   0 4 0
278   1 0 0
279   2 0 0
280   1 1 0
281   0 2 1
282   3 5 4
283   )";
284 
285   IMeshBuilder mb(spec);
286   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
287   out.populate_vert();
288   EXPECT_EQ(out.vert_size(), 6);
289   EXPECT_EQ(out.face_size(), 6);
290   if (out.vert_size() == 6 && out.face_size() == 6) {
291     const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0));
292     const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0));
293     const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0));
294     const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0));
295     const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0));
296     const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0));
297     EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr);
298     EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr);
299     if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr &&
300         v5 != nullptr) {
301       EXPECT_EQ(v0->orig, 0);
302       EXPECT_EQ(v1->orig, 1);
303       const Face *f0 = find_tri_with_verts(out, v4, v5, v1);
304       const Face *f1 = find_tri_with_verts(out, v3, v5, v4);
305       const Face *f2 = find_tri_with_verts(out, v0, v5, v3);
306       const Face *f3 = find_tri_with_verts(out, v0, v2, v5);
307       const Face *f4 = find_tri_with_verts(out, v5, v2, v1);
308       EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr &&
309                   f4 != nullptr);
310       /* For boolean to work right, there need to be two copies of the smaller triangle in the
311        * output. */
312       EXPECT_EQ(count_tris_with_verts(out, v3, v5, v4), 2);
313       if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) {
314         EXPECT_EQ(f0->orig, 0);
315         EXPECT_TRUE(f1->orig == 0 || f1->orig == 1);
316         EXPECT_EQ(f2->orig, 0);
317         EXPECT_EQ(f3->orig, 0);
318         EXPECT_EQ(f4->orig, 0);
319       }
320       int e03 = find_edge_pos_in_tri(v0, v3, f2);
321       int e34 = find_edge_pos_in_tri(v3, v4, f1);
322       int e45 = find_edge_pos_in_tri(v4, v5, f1);
323       int e05 = find_edge_pos_in_tri(v0, v5, f3);
324       int e15 = find_edge_pos_in_tri(v1, v5, f0);
325       EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1);
326       if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) {
327         EXPECT_EQ(f2->edge_orig[e03], 2);
328         EXPECT_TRUE(f1->edge_orig[e34] == 2 ||
329                     f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN + 2);
330         EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1);
331         EXPECT_EQ(f3->edge_orig[e05], NO_INDEX);
332         EXPECT_EQ(f0->edge_orig[e15], NO_INDEX);
333       }
334     }
335   }
336   if (DO_OBJ) {
337     write_obj_mesh(out, "tritrirev");
338   }
339 }
340 
TEST(mesh_intersect,TwoTris)341 TEST(mesh_intersect, TwoTris)
342 {
343   Array<mpq3> verts = {
344       mpq3(1, 1, 1),     mpq3(1, 4, 1),   mpq3(1, 1, 4),  /* T0 */
345       mpq3(2, 2, 2),     mpq3(-3, 3, 2),  mpq3(-4, 1, 3), /* T1 */
346       mpq3(2, 2, 2),     mpq3(-3, 3, 2),  mpq3(0, 3, 5),  /* T2 */
347       mpq3(2, 2, 2),     mpq3(-3, 3, 2),  mpq3(0, 3, 3),  /* T3 */
348       mpq3(1, 0, 0),     mpq3(2, 4, 1),   mpq3(-3, 2, 2), /* T4 */
349       mpq3(0, 2, 1),     mpq3(-2, 3, 3),  mpq3(0, 1, 3),  /* T5 */
350       mpq3(1.5, 2, 0.5), mpq3(-2, 3, 3),  mpq3(0, 1, 3),  /* T6 */
351       mpq3(1, 0, 0),     mpq3(-2, 3, 3),  mpq3(0, 1, 3),  /* T7 */
352       mpq3(1, 0, 0),     mpq3(-3, 2, 2),  mpq3(0, 1, 3),  /* T8 */
353       mpq3(1, 0, 0),     mpq3(-1, 1, 1),  mpq3(0, 1, 3),  /* T9 */
354       mpq3(3, -1, -1),   mpq3(-1, 1, 1),  mpq3(0, 1, 3),  /* T10 */
355       mpq3(0, 0.5, 0.5), mpq3(-1, 1, 1),  mpq3(0, 1, 3),  /* T11 */
356       mpq3(2, 1, 1),     mpq3(3, 5, 2),   mpq3(-2, 3, 3), /* T12 */
357       mpq3(2, 1, 1),     mpq3(3, 5, 2),   mpq3(-2, 3, 4), /* T13 */
358       mpq3(2, 2, 5),     mpq3(-3, 3, 5),  mpq3(0, 3, 10), /* T14 */
359       mpq3(0, 0, 0),     mpq3(4, 4, 0),   mpq3(-4, 2, 4), /* T15 */
360       mpq3(0, 1.5, 1),   mpq3(1, 2.5, 1), mpq3(-1, 2, 2), /* T16 */
361       mpq3(3, 0, -2),    mpq3(7, 4, -2),  mpq3(-1, 2, 2), /* T17 */
362       mpq3(3, 0, -2),    mpq3(3, 6, 2),   mpq3(-1, 2, 2), /* T18 */
363       mpq3(7, 4, -2),    mpq3(3, 6, 2),   mpq3(-1, 2, 2), /* T19 */
364       mpq3(5, 2, -2),    mpq3(1, 4, 2),   mpq3(-3, 0, 2), /* T20 */
365       mpq3(2, 2, 0),     mpq3(1, 4, 2),   mpq3(-3, 0, 2), /* T21 */
366       mpq3(0, 0, 0),     mpq3(4, 4, 0),   mpq3(-3, 0, 2), /* T22 */
367       mpq3(0, 0, 0),     mpq3(4, 4, 0),   mpq3(-1, 2, 2), /* T23 */
368       mpq3(2, 2, 0),     mpq3(4, 4, 0),   mpq3(0, 3, 2),  /* T24 */
369       mpq3(0, 0, 0),     mpq3(-4, 2, 4),  mpq3(4, 4, 0),  /* T25 */
370   };
371   struct two_tri_test_spec {
372     int t0;
373     int t1;
374     int nv_out;
375     int nf_out;
376   };
377   Array<two_tri_test_spec> test_tris = Span<two_tri_test_spec>{
378       {0, 1, 8, 8},  /* 0: T1 pierces T0 inside at (1,11/6,13/6) and (1,11/5,2). */
379       {0, 2, 8, 8},  /* 1: T2 intersects T0 inside (1,11/5,2) and edge (1,7/3,8/3). */
380       {0, 3, 8, 7},  /* 2: T3 intersects T0 (1,11/5,2) and edge-edge (1,5/2,5/2). */
381       {4, 5, 6, 4},  /* 3: T5 touches T4 inside (0,2,1). */
382       {4, 6, 6, 3},  /* 4: T6 touches T4 on edge (3/2,2/1/2). */
383       {4, 7, 5, 2},  /* 5: T7 touches T4 on vert (1,0,0). */
384       {4, 8, 4, 2},  /* 6: T8 shared edge with T4 (1,0,0)(-3,2,2). */
385       {4, 9, 5, 3},  /* 7: T9 edge (1,0,0)(-1,1,1) is subset of T4 edge. */
386       {4, 10, 6, 4}, /* 8: T10 edge overlaps T4 edge with seg (-1,1,0)(1,0,0). */
387       {4, 11, 6, 4}, /* 9: T11 edge (-1,1,1)(0,1/2,1/2) inside T4 edge. */
388       {4, 12, 6, 2}, /* 10: parallel planes, not intersecting. */
389       {4, 13, 6, 2}, /* 11: non-parallel planes, not intersecting, all one side. */
390       {0, 14, 6, 2}, /* 12: non-paralel planes, not intersecting, alternate sides. */
391       /* Following are all coplanar cases. */
392       {15, 16, 6, 8},   /* 13: T16 inside T15. Note: dup'd tri is expected.  */
393       {15, 17, 8, 8},   /* 14: T17 intersects one edge of T15 at (1,1,0)(3,3,0). */
394       {15, 18, 10, 12}, /* 15: T18 intersects T15 at (1,1,0)(3,3,0)(3,15/4,1/2)(0,3,2). */
395       {15, 19, 8, 10},  /* 16: T19 intersects T15 at (3,3,0)(0,3,2). */
396       {15, 20, 12, 14}, /* 17: T20 intersects T15 on three edges, six intersects. */
397       {15, 21, 10, 11}, /* 18: T21 intersects T15 on three edges, touching one. */
398       {15, 22, 5, 4},   /* 19: T22 shares edge T15, one other outside. */
399       {15, 23, 4, 4},   /* 20: T23 shares edge T15, one other outside. */
400       {15, 24, 5, 4},   /* 21: T24 shares two edges with T15. */
401       {15, 25, 3, 2},   /* 22: T25 same T15, reverse orientation. */
402   };
403   static int perms[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};
404 
405   const int do_only_test = -1; /* Make this negative to do all tests. */
406   for (int test = 0; test < test_tris.size(); ++test) {
407     if (do_only_test >= 0 && test != do_only_test) {
408       continue;
409     }
410     int tri1_index = test_tris[test].t0;
411     int tri2_index = test_tris[test].t1;
412     int co1_i = 3 * tri1_index;
413     int co2_i = 3 * tri2_index;
414 
415     const bool verbose = false;
416 
417     if (verbose) {
418       std::cout << "\nTest " << test << ": T" << tri1_index << " intersect T" << tri2_index
419                 << "\n";
420     }
421 
422     const bool do_all_perms = true;
423     const int perm_limit = do_all_perms ? 3 : 1;
424 
425     for (int i = 0; i < perm_limit; ++i) {
426       for (int j = 0; j < perm_limit; ++j) {
427         if (do_all_perms && verbose) {
428           std::cout << "\nperms " << i << " " << j << "\n";
429         }
430         IMeshArena arena;
431         arena.reserve(2 * 3, 2);
432         Array<const Vert *> f0_verts(3);
433         Array<const Vert *> f1_verts(3);
434         for (int k = 0; k < 3; ++k) {
435           f0_verts[k] = arena.add_or_find_vert(verts[co1_i + perms[i][k]], k);
436         }
437         for (int k = 0; k < 3; ++k) {
438           f1_verts[k] = arena.add_or_find_vert(verts[co2_i + perms[i][k]], k + 3);
439         }
440         Face *f0 = arena.add_face(f0_verts, 0, {0, 1, 2});
441         Face *f1 = arena.add_face(f1_verts, 1, {3, 4, 5});
442         IMesh in_mesh({f0, f1});
443         IMesh out_mesh = trimesh_self_intersect(in_mesh, &arena);
444         out_mesh.populate_vert();
445         EXPECT_EQ(out_mesh.vert_size(), test_tris[test].nv_out);
446         EXPECT_EQ(out_mesh.face_size(), test_tris[test].nf_out);
447         bool constexpr dump_input = true;
448         if (DO_OBJ && i == 0 && j == 0) {
449           if (dump_input) {
450             std::string name = "test_tt_in" + std::to_string(test);
451             write_obj_mesh(in_mesh, name);
452           }
453           std::string name = "test_tt" + std::to_string(test);
454           write_obj_mesh(out_mesh, name);
455         }
456       }
457     }
458   }
459 }
460 
TEST(mesh_intersect,OverlapCluster)461 TEST(mesh_intersect, OverlapCluster)
462 {
463   /* Chain of 5 overlapping coplanar tris.
464    * Ordered so that clustering will make two separate clusters
465    * that it will have to merge into one cluster with everything. */
466   const char *spec = R"(15 5
467   0 0 0
468   1 0 0
469   1/2 1 0
470   1/2 0 0
471   3/2 0 0
472   1 1 0
473   1 0 0
474   2 0 0
475   3/2 1 0
476   3/2 0 0
477   5/2 0 0
478   2 1 0
479   2 0 0
480   3 0 0
481   5/2 1 0
482   0 1 2
483   3 4 5
484   9 10 11
485   12 13 14
486   6 7 8
487   )";
488 
489   IMeshBuilder mb(spec);
490   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
491   out.populate_vert();
492   EXPECT_EQ(out.vert_size(), 16);
493   EXPECT_EQ(out.face_size(), 18);
494   if (DO_OBJ) {
495     write_obj_mesh(out, "overlapcluster");
496   }
497 }
498 
TEST(mesh_intersect,TriCornerCross1)499 TEST(mesh_intersect, TriCornerCross1)
500 {
501   /* A corner formed by 3 tris, and a 4th crossing two of them. */
502   const char *spec = R"(12 4
503   0 0 0
504   1 0 0
505   0 0 1
506   0 0 0
507   0 1 0
508   0 0 1
509   0 0 0
510   1 0 0
511   0 1 0
512   1 1 1/2
513   1 -2 1/2
514   -2 1 1/2
515   0 1 2
516   3 4 5
517   6 7 8
518   9 10 11
519   )";
520 
521   IMeshBuilder mb(spec);
522   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
523   out.populate_vert();
524   EXPECT_EQ(out.vert_size(), 10);
525   EXPECT_EQ(out.face_size(), 14);
526   if (DO_OBJ) {
527     write_obj_mesh(out, "test_tc_1");
528   }
529 }
530 
TEST(mesh_intersect,TriCornerCross2)531 TEST(mesh_intersect, TriCornerCross2)
532 {
533   /* A corner formed by 3 tris, and a 4th coplanar with base. */
534   const char *spec = R"(12 4
535   0 0 0
536   1 0 0
537   0 0 1
538   0 0 0
539   0 1 0
540   0 0 1
541   0 0 0
542   1 0 0
543   0 1 0
544   1 1 0
545   1 -2 0
546   -2 1 0
547   0 1 2
548   3 4 5
549   6 7 8
550   9 10 11
551   )";
552 
553   IMeshBuilder mb(spec);
554   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
555   out.populate_vert();
556   EXPECT_EQ(out.vert_size(), 7);
557   EXPECT_EQ(out.face_size(), 8);
558   if (DO_OBJ) {
559     write_obj_mesh(out, "test_tc_2");
560   }
561 }
562 
TEST(mesh_intersect,TriCornerCross3)563 TEST(mesh_intersect, TriCornerCross3)
564 {
565   /* A corner formed by 3 tris, and a 4th crossing all 3. */
566   const char *spec = R"(12 4
567   0 0 0
568   1 0 0
569   0 0 1
570   0 0 0
571   0 1 0
572   0 0 1
573   0 0 0
574   1 0 0
575   0 1 0
576   3/2 -1/2 -1/4
577   -1/2 3/2 -1/4
578   -1/2 -1/2 3/4
579   0 1 2
580   3 4 5
581   6 7 8
582   9 10 11
583   )";
584 
585   IMeshBuilder mb(spec);
586   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
587   out.populate_vert();
588   EXPECT_EQ(out.vert_size(), 10);
589   EXPECT_EQ(out.face_size(), 16);
590   if (DO_OBJ) {
591     write_obj_mesh(out, "test_tc_3");
592   }
593 }
594 
TEST(mesh_intersect,TetTet)595 TEST(mesh_intersect, TetTet)
596 {
597   const char *spec = R"(8 8
598   0 0 0
599   2 0 0
600   1 2 0
601   1 1 2
602   0 0 1
603   2 0 1
604   1 2 1
605   1 1 3
606   0 1 2
607   0 3 1
608   1 3 2
609   2 3 0
610   4 5 6
611   4 7 5
612   5 7 6
613   6 7 4
614   )";
615 
616   IMeshBuilder mb(spec);
617   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
618   out.populate_vert();
619   EXPECT_EQ(out.vert_size(), 11);
620   EXPECT_EQ(out.face_size(), 20);
621   /* Expect there to be a triangle with these three verts, oriented this way, with original face 1.
622    */
623   const Vert *v1 = mb.arena.find_vert(mpq3(2, 0, 0));
624   const Vert *v8 = mb.arena.find_vert(mpq3(0.5, 0.5, 1));
625   const Vert *v9 = mb.arena.find_vert(mpq3(1.5, 0.5, 1));
626   EXPECT_TRUE(v1 != nullptr && v8 != nullptr && v9 != nullptr);
627   const Face *f = mb.arena.find_face({v1, v8, v9});
628   EXPECT_NE(f, nullptr);
629   EXPECT_EQ(f->orig, 1);
630   int v1pos = f->vert[0] == v1 ? 0 : (f->vert[1] == v1 ? 1 : 2);
631   EXPECT_EQ(f->edge_orig[v1pos], NO_INDEX);
632   EXPECT_EQ(f->edge_orig[(v1pos + 1) % 3], NO_INDEX);
633   EXPECT_EQ(f->edge_orig[(v1pos + 2) % 3], 1001);
634   EXPECT_EQ(f->is_intersect[v1pos], false);
635   EXPECT_EQ(f->is_intersect[(v1pos + 1) % 3], true);
636   EXPECT_EQ(f->is_intersect[(v1pos + 2) % 3], false);
637   if (DO_OBJ) {
638     write_obj_mesh(out, "test_tc_3");
639   }
640 }
641 
TEST(mesh_intersect,CubeCubeStep)642 TEST(mesh_intersect, CubeCubeStep)
643 {
644   const char *spec = R"(16 24
645   0 -1 0
646   0 -1 2
647   0 1 0
648   0 1 2
649   2 -1 0
650   2 -1 2
651   2 1 0
652   2 1 2
653   -1 -1 -1
654   -1 -1 1
655   -1 1 -1
656   -1 1 1
657   1 -1 -1
658   1 -1 1
659   1 1 -1
660   1 1 1
661   0 1 3
662   0 3 2
663   2 3 7
664   2 7 6
665   6 7 5
666   6 5 4
667   4 5 1
668   4 1 0
669   2 6 4
670   2 4 0
671   7 3 1
672   7 1 5
673   8 9 11
674   8 11 10
675   10 11 15
676   10 15 14
677   14 15 13
678   14 13 12
679   12 13 9
680   12 9 8
681   10 14 12
682   10 12 8
683   15 11 9
684   15 9 13
685   )";
686 
687   IMeshBuilder mb(spec);
688   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
689   out.populate_vert();
690   EXPECT_EQ(out.vert_size(), 22);
691   EXPECT_EQ(out.face_size(), 56);
692   if (DO_OBJ) {
693     write_obj_mesh(out, "test_cubecubestep");
694   }
695 
696   IMeshBuilder mb2(spec);
697   IMesh out2 = trimesh_nary_intersect(
698       mb2.imesh, 2, [](int t) { return t < 12 ? 0 : 1; }, false, &mb2.arena);
699   out2.populate_vert();
700   EXPECT_EQ(out2.vert_size(), 22);
701   EXPECT_EQ(out2.face_size(), 56);
702   if (DO_OBJ) {
703     write_obj_mesh(out2, "test_cubecubestep_nary");
704   }
705 }
706 
TEST(mesh_intersect,RectCross)707 TEST(mesh_intersect, RectCross)
708 {
709   const char *spec = R"(8 4
710   3/2 0 1
711   -3/2 0 1
712   -3/2 0 -1
713   3/2 0 -1
714   1 0 -5
715   -1 0 -5
716   1 0 5
717   -1 0 5
718   1 0 3
719   1 3 2
720   5 4 6
721   5 6 7
722   )";
723 
724   IMeshBuilder mb(spec);
725   IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena);
726   out.populate_vert();
727   EXPECT_EQ(out.vert_size(), 17);
728   EXPECT_EQ(out.face_size(), 28);
729   if (DO_OBJ) {
730     write_obj_mesh(out, "test_rectcross");
731   }
732 }
733 #  endif
734 
735 #  if DO_PERF_TESTS
736 
get_sphere_params(int nrings,int nsegs,bool triangulate,int * r_num_verts,int * r_num_faces)737 static void get_sphere_params(
738     int nrings, int nsegs, bool triangulate, int *r_num_verts, int *r_num_faces)
739 {
740   *r_num_verts = nsegs * (nrings - 1) + 2;
741   if (triangulate) {
742     *r_num_faces = 2 * nsegs + 2 * nsegs * (nrings - 2);
743   }
744   else {
745     *r_num_faces = nsegs * nrings;
746   }
747 }
748 
fill_sphere_data(int nrings,int nsegs,const double3 & center,double radius,bool triangulate,MutableSpan<Face * > face,int vid_start,int fid_start,IMeshArena * arena)749 static void fill_sphere_data(int nrings,
750                              int nsegs,
751                              const double3 &center,
752                              double radius,
753                              bool triangulate,
754                              MutableSpan<Face *> face,
755                              int vid_start,
756                              int fid_start,
757                              IMeshArena *arena)
758 {
759   int num_verts;
760   int num_faces;
761   get_sphere_params(nrings, nsegs, triangulate, &num_verts, &num_faces);
762   BLI_assert(num_faces == face.size());
763   Array<const Vert *> vert(num_verts);
764   const bool nrings_even = (nrings % 2 == 0);
765   int half_nrings = nrings / 2;
766   const bool nsegs_even = (nsegs % 2) == 0;
767   const bool nsegs_four_divisible = (nsegs % 4 == 0);
768   int half_nsegs = nrings;
769   int quarter_nsegs = half_nsegs / 2;
770   double delta_phi = 2 * M_PI / nsegs;
771   double delta_theta = M_PI / nrings;
772   int fid = fid_start;
773   int vid = vid_start;
774   auto vert_index_fn = [nrings, num_verts](int seg, int ring) {
775     if (ring == 0) { /* Top vert. */
776       return num_verts - 2;
777     }
778     if (ring == nrings) { /* Bottom vert. */
779       return num_verts - 1;
780     }
781     return seg * (nrings - 1) + (ring - 1);
782   };
783   auto face_index_fn = [nrings](int seg, int ring) { return seg * nrings + ring; };
784   auto tri_index_fn = [nrings, nsegs](int seg, int ring, int tri) {
785     if (ring == 0) {
786       return seg;
787     }
788     if (ring < nrings - 1) {
789       return nsegs + 2 * (ring - 1) * nsegs + 2 * seg + tri;
790     }
791     return nsegs + 2 * (nrings - 2) * nsegs + seg;
792   };
793   Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */
794   /*
795    * (x, y , z) is given from inclination theta and azimuth phi,
796    * where 0 <= theta <= pi;  0 <= phi <= 2pi.
797    * x = radius * sin(theta) cos(phi)
798    * y = radius * sin(theta) sin(phi)
799    * z = radius * cos(theta)
800    */
801   for (int s = 0; s < nsegs; ++s) {
802     double phi = s * delta_phi;
803     double sin_phi;
804     double cos_phi;
805     /* Avoid use of trig functions for pi/2 divisible angles. */
806     if (s == 0) {
807       /* phi = 0. */
808       sin_phi = 0.0;
809       cos_phi = 1.0;
810     }
811     else if (nsegs_even && s == half_nsegs) {
812       /* phi = pi. */
813       sin_phi = 0.0;
814       cos_phi = -1.0;
815     }
816     else if (nsegs_four_divisible && s == quarter_nsegs) {
817       /* phi = pi/2. */
818       sin_phi = 1.0;
819       cos_phi = 0.0;
820     }
821     else if (nsegs_four_divisible && s == 3 * quarter_nsegs) {
822       /* phi = 3pi/2. */
823       sin_phi = -1.0;
824       cos_phi = 0.0;
825     }
826     else {
827       sin_phi = sin(phi);
828       cos_phi = cos(phi);
829     }
830     for (int r = 1; r < nrings; ++r) {
831       double theta = r * delta_theta;
832       double r_sin_theta;
833       double r_cos_theta;
834       if (nrings_even && r == half_nrings) {
835         /* theta = pi/2. */
836         r_sin_theta = radius;
837         r_cos_theta = 0.0;
838       }
839       else {
840         r_sin_theta = radius * sin(theta);
841         r_cos_theta = radius * cos(theta);
842       }
843       double x = r_sin_theta * cos_phi + center[0];
844       double y = r_sin_theta * sin_phi + center[1];
845       double z = r_cos_theta + center[2];
846       const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++);
847       vert[vert_index_fn(s, r)] = v;
848     }
849   }
850   const Vert *vtop = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] + radius),
851                                              vid++);
852   const Vert *vbot = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] - radius),
853                                              vid++);
854   vert[vert_index_fn(0, 0)] = vtop;
855   vert[vert_index_fn(0, nrings)] = vbot;
856   for (int s = 0; s < nsegs; ++s) {
857     int snext = (s + 1) % nsegs;
858     for (int r = 0; r < nrings; ++r) {
859       int rnext = r + 1;
860       int i0 = vert_index_fn(s, r);
861       int i1 = vert_index_fn(s, rnext);
862       int i2 = vert_index_fn(snext, rnext);
863       int i3 = vert_index_fn(snext, r);
864       Face *f;
865       Face *f2 = nullptr;
866       if (r == 0) {
867         f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
868       }
869       else if (r == nrings - 1) {
870         f = arena->add_face({vert[i0], vert[i1], vert[i3]}, fid++, eid);
871       }
872       else {
873         if (triangulate) {
874           f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
875           f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid);
876         }
877         else {
878           f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid);
879         }
880       }
881       if (triangulate) {
882         int f_index = tri_index_fn(s, r, 0);
883         face[f_index] = f;
884         if (r != 0 && r != nrings - 1) {
885           int f_index2 = tri_index_fn(s, r, 1);
886           face[f_index2] = f2;
887         }
888       }
889       else {
890         int f_index = face_index_fn(s, r);
891         face[f_index] = f;
892       }
893     }
894   }
895 }
896 
spheresphere_test(int nrings,double y_offset,bool use_self)897 static void spheresphere_test(int nrings, double y_offset, bool use_self)
898 {
899   /* Make two uvspheres with nrings rings ad 2*nrings segments. */
900   if (nrings < 2) {
901     return;
902   }
903   BLI_task_scheduler_init(); /* Without this, no parallelism. */
904   double time_start = PIL_check_seconds_timer();
905   IMeshArena arena;
906   int nsegs = 2 * nrings;
907   int num_sphere_verts;
908   int num_sphere_tris;
909   get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris);
910   Array<Face *> tris(2 * num_sphere_tris);
911   arena.reserve(2 * num_sphere_verts, 2 * num_sphere_tris);
912   double3 center1(0.0, 0.0, 0.0);
913   fill_sphere_data(nrings,
914                    nsegs,
915                    center1,
916                    1.0,
917                    true,
918                    MutableSpan<Face *>(tris.begin(), num_sphere_tris),
919                    0,
920                    0,
921                    &arena);
922   double3 center2(0.0, y_offset, 0.0);
923   fill_sphere_data(nrings,
924                    nsegs,
925                    center2,
926                    1.0,
927                    true,
928                    MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_sphere_tris),
929                    num_sphere_verts,
930                    num_sphere_verts,
931                    &arena);
932   IMesh mesh(tris);
933   double time_create = PIL_check_seconds_timer();
934   // write_obj_mesh(mesh, "spheresphere_in");
935   IMesh out;
936   if (use_self) {
937     out = trimesh_self_intersect(mesh, &arena);
938   }
939   else {
940     int nf = num_sphere_tris;
941     out = trimesh_nary_intersect(
942         mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena);
943   }
944   double time_intersect = PIL_check_seconds_timer();
945   std::cout << "Create time: " << time_create - time_start << "\n";
946   std::cout << "Intersect time: " << time_intersect - time_create << "\n";
947   std::cout << "Total time: " << time_intersect - time_start << "\n";
948   if (DO_OBJ) {
949     write_obj_mesh(out, "spheresphere");
950   }
951   BLI_task_scheduler_exit();
952 }
953 
get_grid_params(int x_subdiv,int y_subdiv,bool triangulate,int * r_num_verts,int * r_num_faces)954 static void get_grid_params(
955     int x_subdiv, int y_subdiv, bool triangulate, int *r_num_verts, int *r_num_faces)
956 {
957   *r_num_verts = x_subdiv * y_subdiv;
958   if (triangulate) {
959     *r_num_faces = 2 * (x_subdiv - 1) * (y_subdiv - 1);
960   }
961   else {
962     *r_num_faces = (x_subdiv - 1) * (y_subdiv - 1);
963   }
964 }
965 
fill_grid_data(int x_subdiv,int y_subdiv,bool triangulate,double size,const double3 & center,double rot_deg,MutableSpan<Face * > face,int vid_start,int fid_start,IMeshArena * arena)966 static void fill_grid_data(int x_subdiv,
967                            int y_subdiv,
968                            bool triangulate,
969                            double size,
970                            const double3 &center,
971                            double rot_deg,
972                            MutableSpan<Face *> face,
973                            int vid_start,
974                            int fid_start,
975                            IMeshArena *arena)
976 {
977   if (x_subdiv <= 1 || y_subdiv <= 1) {
978     return;
979   }
980   int num_verts;
981   int num_faces;
982   get_grid_params(x_subdiv, y_subdiv, triangulate, &num_verts, &num_faces);
983   BLI_assert(face.size() == num_faces);
984   Array<const Vert *> vert(num_verts);
985   auto vert_index_fn = [x_subdiv](int ix, int iy) { return iy * x_subdiv + ix; };
986   auto face_index_fn = [x_subdiv](int ix, int iy) { return iy * (x_subdiv - 1) + ix; };
987   auto tri_index_fn = [x_subdiv](int ix, int iy, int tri) {
988     return 2 * iy * (x_subdiv - 1) + 2 * ix + tri;
989   };
990   Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */
991   double r = size / 2.0;
992   double delta_x = size / (x_subdiv - 1);
993   double delta_y = size / (y_subdiv - 1);
994   int vid = vid_start;
995   double cos_rot = cosf(rot_deg * M_PI / 180.0);
996   double sin_rot = sinf(rot_deg * M_PI / 180.0);
997   for (int iy = 0; iy < y_subdiv; ++iy) {
998     double yy = iy * delta_y - r;
999     for (int ix = 0; ix < x_subdiv; ++ix) {
1000       double xx = ix * delta_x - r;
1001       double x = center[0] + xx;
1002       double y = center[1] + yy;
1003       double z = center[2];
1004       if (rot_deg != 0.0) {
1005         x = center[0] + xx * cos_rot - yy * sin_rot;
1006         y = center[1] + xx * sin_rot + yy * cos_rot;
1007       }
1008       const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++);
1009       vert[vert_index_fn(ix, iy)] = v;
1010     }
1011   }
1012   int fid = fid_start;
1013   for (int iy = 0; iy < y_subdiv - 1; ++iy) {
1014     for (int ix = 0; ix < x_subdiv - 1; ++ix) {
1015       int i0 = vert_index_fn(ix, iy);
1016       int i1 = vert_index_fn(ix, iy + 1);
1017       int i2 = vert_index_fn(ix + 1, iy + 1);
1018       int i3 = vert_index_fn(ix + 1, iy);
1019       if (triangulate) {
1020         Face *f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid);
1021         Face *f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid);
1022         face[tri_index_fn(ix, iy, 0)] = f;
1023         face[tri_index_fn(ix, iy, 1)] = f2;
1024       }
1025       else {
1026         Face *f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid);
1027         face[face_index_fn(ix, iy)] = f;
1028       }
1029     }
1030   }
1031 }
1032 
spheregrid_test(int nrings,int grid_level,double z_offset,bool use_self)1033 static void spheregrid_test(int nrings, int grid_level, double z_offset, bool use_self)
1034 {
1035   /* Make a uv-sphere and a grid.
1036    * The sphere is radius 1, has `nrings` rings and `2 * nrings` segments,
1037    * and is centered at (0,0,z_offset).
1038    * The plane is 4x4, has `2 ** grid_level` subdivisions x and y,
1039    * and is centered at the origin. */
1040   if (nrings < 2 || grid_level < 1) {
1041     return;
1042   }
1043   BLI_task_scheduler_init(); /* Without this, no parallelism. */
1044   double time_start = PIL_check_seconds_timer();
1045   IMeshArena arena;
1046   int num_sphere_verts;
1047   int num_sphere_tris;
1048   int nsegs = 2 * nrings;
1049   int num_grid_verts;
1050   int num_grid_tris;
1051   int subdivs = 1 << grid_level;
1052   get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris);
1053   get_grid_params(subdivs, subdivs, true, &num_grid_verts, &num_grid_tris);
1054   Array<Face *> tris(num_sphere_tris + num_grid_tris);
1055   arena.reserve(num_sphere_verts + num_grid_verts, num_sphere_tris + num_grid_tris);
1056   double3 center(0.0, 0.0, z_offset);
1057   fill_sphere_data(nrings,
1058                    nsegs,
1059                    center,
1060                    1.0,
1061                    true,
1062                    MutableSpan<Face *>(tris.begin(), num_sphere_tris),
1063                    0,
1064                    0,
1065                    &arena);
1066   fill_grid_data(subdivs,
1067                  subdivs,
1068                  true,
1069                  4.0,
1070                  double3(0, 0, 0),
1071                  0.0,
1072                  MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_grid_tris),
1073                  num_sphere_verts,
1074                  num_sphere_tris,
1075                  &arena);
1076   IMesh mesh(tris);
1077   double time_create = PIL_check_seconds_timer();
1078   // write_obj_mesh(mesh, "spheregrid_in");
1079   IMesh out;
1080   if (use_self) {
1081     out = trimesh_self_intersect(mesh, &arena);
1082   }
1083   else {
1084     int nf = num_sphere_tris;
1085     out = trimesh_nary_intersect(
1086         mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena);
1087   }
1088   double time_intersect = PIL_check_seconds_timer();
1089   std::cout << "Create time: " << time_create - time_start << "\n";
1090   std::cout << "Intersect time: " << time_intersect - time_create << "\n";
1091   std::cout << "Total time: " << time_intersect - time_start << "\n";
1092   if (DO_OBJ) {
1093     write_obj_mesh(out, "spheregrid");
1094   }
1095   BLI_task_scheduler_exit();
1096 }
1097 
gridgrid_test(int x_level_1,int y_level_1,int x_level_2,int y_level_2,double x_off,double y_off,double rot_deg,bool use_self)1098 static void gridgrid_test(int x_level_1,
1099                           int y_level_1,
1100                           int x_level_2,
1101                           int y_level_2,
1102                           double x_off,
1103                           double y_off,
1104                           double rot_deg,
1105                           bool use_self)
1106 {
1107   /* Make two grids, each 4x4, with given subdivision levels in x and y,
1108    * and the second offset from the first by x_off, y_off, and rotated by rot_deg degrees. */
1109   BLI_task_scheduler_init(); /* Without this, no parallelism. */
1110   double time_start = PIL_check_seconds_timer();
1111   IMeshArena arena;
1112   int x_subdivs_1 = 1 << x_level_1;
1113   int y_subdivs_1 = 1 << y_level_1;
1114   int x_subdivs_2 = 1 << x_level_2;
1115   int y_subdivs_2 = 1 << y_level_2;
1116   int num_grid_verts_1;
1117   int num_grid_verts_2;
1118   int num_grid_tris_1;
1119   int num_grid_tris_2;
1120   get_grid_params(x_subdivs_1, y_subdivs_1, true, &num_grid_verts_1, &num_grid_tris_1);
1121   get_grid_params(x_subdivs_2, y_subdivs_2, true, &num_grid_verts_2, &num_grid_tris_2);
1122   Array<Face *> tris(num_grid_tris_1 + num_grid_tris_2);
1123   arena.reserve(num_grid_verts_1 + num_grid_verts_2, num_grid_tris_1 + num_grid_tris_2);
1124   fill_grid_data(x_subdivs_1,
1125                  y_subdivs_1,
1126                  true,
1127                  4.0,
1128                  double3(0, 0, 0),
1129                  0.0,
1130                  MutableSpan<Face *>(tris.begin(), num_grid_tris_1),
1131                  0,
1132                  0,
1133                  &arena);
1134   fill_grid_data(x_subdivs_2,
1135                  y_subdivs_2,
1136                  true,
1137                  4.0,
1138                  double3(x_off, y_off, 0),
1139                  rot_deg,
1140                  MutableSpan<Face *>(tris.begin() + num_grid_tris_1, num_grid_tris_2),
1141                  num_grid_verts_1,
1142                  num_grid_tris_1,
1143                  &arena);
1144   IMesh mesh(tris);
1145   double time_create = PIL_check_seconds_timer();
1146   // write_obj_mesh(mesh, "gridgrid_in");
1147   IMesh out;
1148   if (use_self) {
1149     out = trimesh_self_intersect(mesh, &arena);
1150   }
1151   else {
1152     int nf = num_grid_tris_1;
1153     out = trimesh_nary_intersect(
1154         mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena);
1155   }
1156   double time_intersect = PIL_check_seconds_timer();
1157   std::cout << "Create time: " << time_create - time_start << "\n";
1158   std::cout << "Intersect time: " << time_intersect - time_create << "\n";
1159   std::cout << "Total time: " << time_intersect - time_start << "\n";
1160   if (DO_OBJ) {
1161     write_obj_mesh(out, "gridgrid");
1162   }
1163   BLI_task_scheduler_exit();
1164 }
1165 
TEST(mesh_intersect_perf,SphereSphere)1166 TEST(mesh_intersect_perf, SphereSphere)
1167 {
1168   spheresphere_test(512, 0.5, false);
1169 }
1170 
TEST(mesh_intersect_perf,SphereSphereSelf)1171 TEST(mesh_intersect_perf, SphereSphereSelf)
1172 {
1173   spheresphere_test(64, 0.5, true);
1174 }
1175 
TEST(mesh_intersect_perf,SphereGrid)1176 TEST(mesh_intersect_perf, SphereGrid)
1177 {
1178   spheregrid_test(512, 4, 0.1, false);
1179 }
1180 
TEST(mesh_intersect_perf,SphereGridSelf)1181 TEST(mesh_intersect_perf, SphereGridSelf)
1182 {
1183   spheregrid_test(64, 4, 0.1, true);
1184 }
1185 
TEST(mesh_intersect_perf,GridGrid)1186 TEST(mesh_intersect_perf, GridGrid)
1187 {
1188   gridgrid_test(8, 2, 4, 2, 0.1, 0.1, 0.0, false);
1189 }
1190 
TEST(mesh_intersect_perf,GridGridTilt)1191 TEST(mesh_intersect_perf, GridGridTilt)
1192 {
1193   gridgrid_test(8, 2, 4, 2, 0.0, 0.0, 1.0, false);
1194 }
1195 
1196 #  endif
1197 
1198 }  // namespace blender::meshintersect::tests
1199 #endif
1200