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 ¢er,
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 ¢er,
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