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