Blender V4.3
BLI_mesh_boolean_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 <fstream>
8#include <iostream>
9#include <sstream>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_array.hh"
14#include "BLI_map.hh"
15#include "BLI_math_mpq.hh"
17#include "BLI_mesh_boolean.hh"
18#include "BLI_vector.hh"
19
20#ifdef WITH_GMP
21namespace blender::meshintersect::tests {
22
23constexpr bool DO_OBJ = false;
24
25/* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */
26class IMeshBuilder {
27 public:
28 IMesh imesh;
29 IMeshArena arena;
30
31 /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */
32 static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */
33
34 static int edge_index(int face_index, int facepos)
35 {
36 return face_index * MAX_FACE_LEN + facepos;
37 }
38
39 static std::pair<int, int> face_and_pos_for_edge_index(int e_index)
40 {
41 return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN);
42 }
43
52 IMeshBuilder(const char *spec)
53 {
54 std::istringstream ss(spec);
55 std::string line;
56 getline(ss, line);
57 std::istringstream hdrss(line);
58 int nv, nf;
59 hdrss >> nv >> nf;
60 if (nv == 0 || nf == 0) {
61 return;
62 }
63 arena.reserve(nv, nf);
64 Vector<const Vert *> verts;
65 Vector<Face *> faces;
66 bool spec_ok = true;
67 int v_index = 0;
68 while (v_index < nv && spec_ok && getline(ss, line)) {
69 std::istringstream iss(line);
70 mpq_class p0;
71 mpq_class p1;
72 mpq_class p2;
73 iss >> p0 >> p1 >> p2;
74 spec_ok = !iss.fail();
75 verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index));
76 ++v_index;
77 }
78 if (v_index != nv) {
79 spec_ok = false;
80 }
81 int f_index = 0;
82 while (f_index < nf && spec_ok && getline(ss, line)) {
83 std::istringstream fss(line);
84 Vector<const Vert *> face_verts;
85 Vector<int> edge_orig;
86 int fpos = 0;
87 while (spec_ok && fss >> v_index) {
88 if (v_index < 0 || v_index >= nv) {
89 spec_ok = false;
90 continue;
91 }
92 face_verts.append(verts[v_index]);
93 edge_orig.append(edge_index(f_index, fpos));
94 ++fpos;
95 }
96 Face *facep = arena.add_face(face_verts, f_index, edge_orig);
97 faces.append(facep);
98 ++f_index;
99 }
100 if (f_index != nf) {
101 spec_ok = false;
102 }
103 if (!spec_ok) {
104 std::cout << "Bad spec: " << spec;
105 return;
106 }
107 imesh = IMesh(faces);
108 }
109};
110
111static int all_shape_zero(int /*t*/)
112{
113 return 0;
114}
115
116TEST(boolean_trimesh, Empty)
117{
118 IMeshArena arena;
119 IMesh in;
120 IMesh out = boolean_trimesh(in, BoolOpType::None, 1, all_shape_zero, true, false, &arena);
121 out.populate_vert();
122 EXPECT_EQ(out.vert_size(), 0);
123 EXPECT_EQ(out.face_size(), 0);
124}
125
126TEST(boolean_trimesh, TetTetTrimesh)
127{
128 const char *spec = R"(8 8
129 0 0 0
130 2 0 0
131 1 2 0
132 1 1 2
133 0 0 1
134 2 0 1
135 1 2 1
136 1 1 3
137 0 2 1
138 0 1 3
139 1 2 3
140 2 0 3
141 4 6 5
142 4 5 7
143 5 6 7
144 6 4 7
145 )";
146
147 IMeshBuilder mb(spec);
148 IMesh out = boolean_trimesh(
149 mb.imesh, BoolOpType::None, 1, all_shape_zero, true, false, &mb.arena);
150 out.populate_vert();
151 EXPECT_EQ(out.vert_size(), 11);
152 EXPECT_EQ(out.face_size(), 20);
153 if (DO_OBJ) {
154 write_obj_mesh(out, "tettet_tm");
155 }
156
157 IMeshBuilder mb2(spec);
158 IMesh out2 = boolean_trimesh(
159 mb2.imesh, BoolOpType::Union, 1, all_shape_zero, true, false, &mb2.arena);
160 out2.populate_vert();
161 EXPECT_EQ(out2.vert_size(), 10);
162 EXPECT_EQ(out2.face_size(), 16);
163 if (DO_OBJ) {
164 write_obj_mesh(out2, "tettet_union_tm");
165 }
166
167 IMeshBuilder mb3(spec);
168 IMesh out3 = boolean_trimesh(
169 mb3.imesh,
170 BoolOpType::Union,
171 2,
172 [](int t) { return t < 4 ? 0 : 1; },
173 false,
174 false,
175 &mb3.arena);
176 out3.populate_vert();
177 EXPECT_EQ(out3.vert_size(), 10);
178 EXPECT_EQ(out3.face_size(), 16);
179 if (DO_OBJ) {
180 write_obj_mesh(out3, "tettet_union_binary_tm");
181 }
182
183 IMeshBuilder mb4(spec);
184 IMesh out4 = boolean_trimesh(
185 mb4.imesh,
186 BoolOpType::Union,
187 2,
188 [](int t) { return t < 4 ? 0 : 1; },
189 true,
190 false,
191 &mb4.arena);
192 out4.populate_vert();
193 EXPECT_EQ(out4.vert_size(), 10);
194 EXPECT_EQ(out4.face_size(), 16);
195 if (DO_OBJ) {
196 write_obj_mesh(out4, "tettet_union_binary_self_tm");
197 }
198
199 IMeshBuilder mb5(spec);
200 IMesh out5 = boolean_trimesh(
201 mb5.imesh,
202 BoolOpType::Intersect,
203 2,
204 [](int t) { return t < 4 ? 0 : 1; },
205 false,
206 false,
207 &mb5.arena);
208 out5.populate_vert();
209 EXPECT_EQ(out5.vert_size(), 4);
210 EXPECT_EQ(out5.face_size(), 4);
211 if (DO_OBJ) {
212 write_obj_mesh(out5, "tettet_intersect_binary_tm");
213 }
214
215 IMeshBuilder mb6(spec);
216 IMesh out6 = boolean_trimesh(
217 mb6.imesh,
218 BoolOpType::Difference,
219 2,
220 [](int t) { return t < 4 ? 0 : 1; },
221 false,
222 false,
223 &mb6.arena);
224 out6.populate_vert();
225 EXPECT_EQ(out6.vert_size(), 6);
226 EXPECT_EQ(out6.face_size(), 8);
227 if (DO_OBJ) {
228 write_obj_mesh(out6, "tettet_difference_binary_tm");
229 }
230
231 IMeshBuilder mb7(spec);
232 IMesh out7 = boolean_trimesh(
233 mb7.imesh,
234 BoolOpType::Difference,
235 2,
236 [](int t) { return t < 4 ? 1 : 0; },
237 false,
238 false,
239 &mb7.arena);
240 out7.populate_vert();
241 EXPECT_EQ(out7.vert_size(), 8);
242 EXPECT_EQ(out7.face_size(), 12);
243 if (DO_OBJ) {
244 write_obj_mesh(out7, "tettet_difference_rev_binary_tm");
245 }
246}
247
248TEST(boolean_trimesh, TetTet2Trimesh)
249{
250 const char *spec = R"(8 8
251 0 1 -1
252 7/8 -1/2 -1
253 -7/8 -1/2 -1
254 0 0 1
255 0 1 0
256 7/8 -1/2 0
257 -7/8 -1/2 0
258 0 0 2
259 0 3 1
260 0 1 2
261 1 3 2
262 2 3 0
263 4 7 5
264 4 5 6
265 5 7 6
266 6 7 4
267 )";
268
269 IMeshBuilder mb(spec);
270 IMesh out = boolean_trimesh(
271 mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, false, &mb.arena);
272 out.populate_vert();
273 EXPECT_EQ(out.vert_size(), 10);
274 EXPECT_EQ(out.face_size(), 16);
275 if (DO_OBJ) {
276 write_obj_mesh(out, "tettet2_union_tm");
277 }
278}
279
280TEST(boolean_trimesh, CubeTetTrimesh)
281{
282 const char *spec = R"(12 16
283 -1 -1 -1
284 -1 -1 1
285 -1 1 -1
286 -1 1 1
287 1 -1 -1
288 1 -1 1
289 1 1 -1
290 1 1 1
291 0 1/2 1/2
292 1/2 -1/4 1/2
293 -1/2 -1/4 1/2
294 0 0 3/2
295 0 1 3
296 0 3 2
297 2 3 7
298 2 7 6
299 6 7 5
300 6 5 4
301 4 5 1
302 4 1 0
303 2 6 4
304 2 4 0
305 7 3 1
306 7 1 5
307 8 11 9
308 8 9 10
309 9 11 10
310 10 11 8
311 )";
312
313 IMeshBuilder mb(spec);
314 IMesh out = boolean_trimesh(
315 mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, false, &mb.arena);
316 out.populate_vert();
317 EXPECT_EQ(out.vert_size(), 14);
318 EXPECT_EQ(out.face_size(), 24);
319 if (DO_OBJ) {
320 write_obj_mesh(out, "cubetet_union_tm");
321 }
322}
323
324TEST(boolean_trimesh, BinaryTetTetTrimesh)
325{
326 const char *spec = R"(8 8
327 0 0 0
328 2 0 0
329 1 2 0
330 1 1 2
331 0 0 1
332 2 0 1
333 1 2 1
334 1 1 3
335 0 2 1
336 0 1 3
337 1 2 3
338 2 0 3
339 4 6 5
340 4 5 7
341 5 6 7
342 6 4 7
343 )";
344
345 IMeshBuilder mb(spec);
346 IMesh out = boolean_trimesh(
347 mb.imesh,
348 BoolOpType::Intersect,
349 2,
350 [](int t) { return t < 4 ? 0 : 1; },
351 false,
352 false,
353 &mb.arena);
354 out.populate_vert();
355 EXPECT_EQ(out.vert_size(), 4);
356 EXPECT_EQ(out.face_size(), 4);
357 if (DO_OBJ) {
358 write_obj_mesh(out, "binary_tettet_isect_tm");
359 }
360}
361
362TEST(boolean_trimesh, TetTetCoplanarTrimesh)
363{
364 const char *spec = R"(8 8
365 0 1 0
366 7/8 -1/2 0
367 -7/8 -1/2 0
368 0 0 1
369 0 1 0
370 7/8 -1/2 0
371 -7/8 -1/2 0
372 0 0 -1
373 0 3 1
374 0 1 2
375 1 3 2
376 2 3 0
377 4 5 7
378 4 6 5
379 5 6 7
380 6 4 7
381 )";
382
383 IMeshBuilder mb(spec);
384 IMesh out = boolean_trimesh(
385 mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, false, &mb.arena);
386 out.populate_vert();
387 EXPECT_EQ(out.vert_size(), 5);
388 EXPECT_EQ(out.face_size(), 6);
389 if (DO_OBJ) {
390 write_obj_mesh(out, "tettet_coplanar_tm");
391 }
392}
393
394TEST(boolean_trimesh, TetInsideTetTrimesh)
395{
396 const char *spec = R"(8 8
397 0 0 0
398 2 0 0
399 1 2 0
400 1 1 2
401 -1 -3/4 -1/2
402 3 -3/4 -1/2
403 1 13/4 -1/2
404 1 5/4 7/2
405 0 2 1
406 0 1 3
407 1 2 3
408 2 0 3
409 4 6 5
410 4 5 7
411 5 6 7
412 6 4 7
413 )";
414
415 IMeshBuilder mb(spec);
416 IMesh out = boolean_trimesh(
417 mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, false, &mb.arena);
418 out.populate_vert();
419 EXPECT_EQ(out.vert_size(), 4);
420 EXPECT_EQ(out.face_size(), 4);
421 if (DO_OBJ) {
422 write_obj_mesh(out, "tetinsidetet_tm");
423 }
424}
425
426TEST(boolean_trimesh, TetBesideTetTrimesh)
427{
428 const char *spec = R"(8 8
429 0 0 0
430 2 0 0
431 1 2 0
432 1 1 2
433 3 0 0
434 5 0 0
435 4 2 0
436 4 1 2
437 0 2 1
438 0 1 3
439 1 2 3
440 2 0 3
441 4 6 5
442 4 5 7
443 5 6 7
444 6 4 7
445 )";
446
447 IMeshBuilder mb(spec);
448 IMesh out = boolean_trimesh(
449 mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, false, &mb.arena);
450 out.populate_vert();
451 EXPECT_EQ(out.vert_size(), 8);
452 EXPECT_EQ(out.face_size(), 8);
453 if (DO_OBJ) {
454 write_obj_mesh(out, "tetbesidetet_tm");
455 }
456}
457
458TEST(boolean_trimesh, DegenerateTris)
459{
460 const char *spec = R"(10 10
461 0 0 0
462 2 0 0
463 1 2 0
464 1 1 2
465 0 0 1
466 2 0 1
467 1 2 1
468 1 1 3
469 0 0 0
470 1 0 0
471 0 2 1
472 0 8 1
473 0 1 3
474 1 2 3
475 2 0 3
476 4 6 5
477 4 5 7
478 5 6 7
479 6 4 7
480 0 1 9
481 )";
482
483 IMeshBuilder mb(spec);
484 IMesh out = boolean_trimesh(
485 mb.imesh,
486 BoolOpType::Intersect,
487 2,
488 [](int t) { return t < 5 ? 0 : 1; },
489 false,
490 false,
491 &mb.arena);
492 out.populate_vert();
493 EXPECT_EQ(out.vert_size(), 4);
494 EXPECT_EQ(out.face_size(), 4);
495 if (DO_OBJ) {
496 write_obj_mesh(out, "degenerate_tris_tm");
497 }
498}
499
500TEST(boolean_polymesh, TetTet)
501{
502 const char *spec = R"(8 8
503 0 0 0
504 2 0 0
505 1 2 0
506 1 1 2
507 0 0 1
508 2 0 1
509 1 2 1
510 1 1 3
511 0 2 1
512 0 1 3
513 1 2 3
514 2 0 3
515 4 6 5
516 4 5 7
517 5 6 7
518 6 4 7
519 )";
520
521 IMeshBuilder mb(spec);
522 IMesh out = boolean_mesh(
523 mb.imesh, BoolOpType::None, 1, all_shape_zero, true, false, nullptr, &mb.arena);
524 out.populate_vert();
525 EXPECT_EQ(out.vert_size(), 11);
526 EXPECT_EQ(out.face_size(), 13);
527 if (DO_OBJ) {
528 write_obj_mesh(out, "tettet");
529 }
530
531 IMeshBuilder mb2(spec);
532 IMesh out2 = boolean_mesh(
533 mb2.imesh,
534 BoolOpType::None,
535 2,
536 [](int t) { return t < 4 ? 0 : 1; },
537 false,
538 false,
539 nullptr,
540 &mb2.arena);
541 out2.populate_vert();
542 EXPECT_EQ(out2.vert_size(), 11);
543 EXPECT_EQ(out2.face_size(), 13);
544 if (DO_OBJ) {
545 write_obj_mesh(out, "tettet2");
546 }
547}
548
549TEST(boolean_polymesh, CubeCube)
550{
551 const char *spec = R"(16 12
552 -1 -1 -1
553 -1 -1 1
554 -1 1 -1
555 -1 1 1
556 1 -1 -1
557 1 -1 1
558 1 1 -1
559 1 1 1
560 1/2 1/2 1/2
561 1/2 1/2 5/2
562 1/2 5/2 1/2
563 1/2 5/2 5/2
564 5/2 1/2 1/2
565 5/2 1/2 5/2
566 5/2 5/2 1/2
567 5/2 5/2 5/2
568 0 1 3 2
569 6 2 3 7
570 4 6 7 5
571 0 4 5 1
572 0 2 6 4
573 3 1 5 7
574 8 9 11 10
575 14 10 11 15
576 12 14 15 13
577 8 12 13 9
578 8 10 14 12
579 11 9 13 15
580 )";
581
582 IMeshBuilder mb(spec);
583 if (DO_OBJ) {
584 write_obj_mesh(mb.imesh, "cube_cube_in");
585 }
586 IMesh out = boolean_mesh(
587 mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, false, nullptr, &mb.arena);
588 out.populate_vert();
589 EXPECT_EQ(out.vert_size(), 20);
590 EXPECT_EQ(out.face_size(), 12);
591 if (DO_OBJ) {
592 write_obj_mesh(out, "cubecube_union");
593 }
594
595 IMeshBuilder mb2(spec);
596 IMesh out2 = boolean_mesh(
597 mb2.imesh,
598 BoolOpType::None,
599 2,
600 [](int t) { return t < 6 ? 0 : 1; },
601 false,
602 false,
603 nullptr,
604 &mb2.arena);
605 out2.populate_vert();
606 EXPECT_EQ(out2.vert_size(), 22);
607 EXPECT_EQ(out2.face_size(), 18);
608 if (DO_OBJ) {
609 write_obj_mesh(out2, "cubecube_none");
610 }
611}
612
613TEST(boolean_polymesh, CubeCone)
614{
615 const char *spec = R"(14 12
616 -1 -1 -1
617 -1 -1 1
618 -1 1 -1
619 -1 1 1
620 1 -1 -1
621 1 -1 1
622 1 1 -1
623 1 1 1
624 0 1/2 3/4
625 119/250 31/200 3/4
626 147/500 -81/200 3/4
627 0 0 7/4
628 -147/500 -81/200 3/4
629 -119/250 31/200 3/4
630 0 1 3 2
631 2 3 7 6
632 6 7 5 4
633 4 5 1 0
634 2 6 4 0
635 7 3 1 5
636 8 11 9
637 9 11 10
638 10 11 12
639 12 11 13
640 13 11 8
641 8 9 10 12 13)";
642
643 IMeshBuilder mb(spec);
644 IMesh out = boolean_mesh(
645 mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, false, nullptr, &mb.arena);
646 out.populate_vert();
647 EXPECT_EQ(out.vert_size(), 14);
648 EXPECT_EQ(out.face_size(), 12);
649 if (DO_OBJ) {
650 write_obj_mesh(out, "cubeccone");
651 }
652}
653
654TEST(boolean_polymesh, CubeCubeCoplanar)
655{
656 const char *spec = R"(16 12
657 -1 -1 -1
658 -1 -1 1
659 -1 1 -1
660 -1 1 1
661 1 -1 -1
662 1 -1 1
663 1 1 -1
664 1 1 1
665 -1/2 -1/2 1
666 -1/2 -1/2 2
667 -1/2 1/2 1
668 -1/2 1/2 2
669 1/2 -1/2 1
670 1/2 -1/2 2
671 1/2 1/2 1
672 1/2 1/2 2
673 0 1 3 2
674 2 3 7 6
675 6 7 5 4
676 4 5 1 0
677 2 6 4 0
678 7 3 1 5
679 8 9 11 10
680 10 11 15 14
681 14 15 13 12
682 12 13 9 8
683 10 14 12 8
684 15 11 9 13
685 )";
686
687 IMeshBuilder mb(spec);
688 IMesh out = boolean_mesh(
689 mb.imesh,
690 BoolOpType::Union,
691 2,
692 [](int t) { return t < 6 ? 0 : 1; },
693 false,
694 false,
695 nullptr,
696 &mb.arena);
697 out.populate_vert();
698 EXPECT_EQ(out.vert_size(), 16);
699 EXPECT_EQ(out.face_size(), 12);
700 if (DO_OBJ) {
701 write_obj_mesh(out, "cubecube_coplanar");
702 }
703}
704
705TEST(boolean_polymesh, TetTeTCoplanarDiff)
706{
707 const char *spec = R"(8 8
708 0 1 0
709 7/8 -1/2 0
710 -7/8 -1/2 0
711 0 0 1
712 0 1 0
713 7/8 -1/2 0
714 -7/8 -1/2 0
715 0 0 -1
716 0 3 1
717 0 1 2
718 1 3 2
719 2 3 0
720 4 5 7
721 4 6 5
722 5 6 7
723 6 4 7
724 )";
725
726 IMeshBuilder mb(spec);
727 IMesh out = boolean_mesh(
728 mb.imesh,
729 BoolOpType::Difference,
730 2,
731 [](int t) { return t < 4 ? 0 : 1; },
732 false,
733 false,
734 nullptr,
735 &mb.arena);
736 out.populate_vert();
737 EXPECT_EQ(out.vert_size(), 4);
738 EXPECT_EQ(out.face_size(), 4);
739 if (DO_OBJ) {
740 write_obj_mesh(out, "tettet_coplanar_diff");
741 }
742}
743
744TEST(boolean_polymesh, CubeCubeStep)
745{
746 const char *spec = R"(16 12
747 0 -1 0
748 0 -1 2
749 0 1 0
750 0 1 2
751 2 -1 0
752 2 -1 2
753 2 1 0
754 2 1 2
755 -1 -1 -1
756 -1 -1 1
757 -1 1 -1
758 -1 1 1
759 1 -1 -1
760 1 -1 1
761 1 1 -1
762 1 1 1
763 0 1 3 2
764 2 3 7 6
765 6 7 5 4
766 4 5 1 0
767 2 6 4 0
768 7 3 1 5
769 8 9 11 10
770 10 11 15 14
771 14 15 13 12
772 12 13 9 8
773 10 14 12 8
774 15 11 9 13
775 )";
776
777 IMeshBuilder mb(spec);
778 IMesh out = boolean_mesh(
779 mb.imesh,
780 BoolOpType::Difference,
781 2,
782 [](int t) { return t < 6 ? 0 : 1; },
783 false,
784 false,
785 nullptr,
786 &mb.arena);
787 out.populate_vert();
788 EXPECT_EQ(out.vert_size(), 12);
789 EXPECT_EQ(out.face_size(), 8);
790 if (DO_OBJ) {
791 write_obj_mesh(out, "cubecubestep");
792 }
793}
794
795TEST(boolean_polymesh, CubeCyl4)
796{
797 const char *spec = R"(16 12
798 0 1 -1
799 0 1 1
800 1 0 -1
801 1 0 1
802 0 -1 -1
803 0 -1 1
804 -1 0 -1
805 -1 0 1
806 -1 -1 -1
807 -1 -1 1
808 -1 1 -1
809 -1 1 1
810 1 -1 -1
811 1 -1 1
812 1 1 -1
813 1 1 1
814 0 1 3 2
815 2 3 5 4
816 3 1 7 5
817 4 5 7 6
818 6 7 1 0
819 0 2 4 6
820 8 9 11 10
821 10 11 15 14
822 14 15 13 12
823 12 13 9 8
824 10 14 12 8
825 15 11 9 13
826 )";
827
828 IMeshBuilder mb(spec);
829 IMesh out = boolean_mesh(
830 mb.imesh,
831 BoolOpType::Difference,
832 2,
833 [](int t) { return t < 6 ? 1 : 0; },
834 false,
835 false,
836 nullptr,
837 &mb.arena);
838 out.populate_vert();
839 EXPECT_EQ(out.vert_size(), 16);
840 EXPECT_EQ(out.face_size(), 20);
841 if (DO_OBJ) {
842 write_obj_mesh(out, "cubecyl4");
843 }
844}
845
846TEST(boolean_polymesh, CubeCubesubdivDiff)
847{
848 /* A cube intersected by a subdivided cube that intersects first cubes edges exactly. */
849 const char *spec = R"(26 22
850 2 1/3 2
851 2 -1/3 2
852 2 -1/3 0
853 2 1/3 0
854 0 -1/3 2
855 0 1/3 2
856 0 1/3 0
857 0 -1/3 0
858 1 1/3 2
859 1 -1/3 2
860 1 1/3 0
861 1 -1/3 0
862 0 -1/3 1
863 0 1/3 1
864 2 1/3 1
865 2 -1/3 1
866 1 1/3 1
867 1 -1/3 1
868 -1 -1 -1
869 -1 -1 1
870 -1 1 -1
871 -1 1 1
872 1 -1 -1
873 1 -1 1
874 1 1 -1
875 1 1 1
876 17 9 4 12
877 13 6 7 12
878 15 2 3 14
879 11 7 6 10
880 16 13 5 8
881 9 1 0 8
882 4 9 8 5
883 14 16 8 0
884 2 11 10 3
885 15 1 9 17
886 2 15 17 11
887 3 10 16 14
888 10 6 13 16
889 1 15 14 0
890 5 13 12 4
891 11 17 12 7
892 19 21 20 18
893 21 25 24 20
894 25 23 22 24
895 23 19 18 22
896 18 20 24 22
897 23 25 21 19
898 )";
899
900 IMeshBuilder mb(spec);
901 IMesh out = boolean_mesh(
902 mb.imesh,
903 BoolOpType::Difference,
904 2,
905 [](int t) { return t < 16 ? 1 : 0; },
906 false,
907 false,
908 nullptr,
909 &mb.arena);
910 out.populate_vert();
911 EXPECT_EQ(out.vert_size(), 16);
912 EXPECT_EQ(out.face_size(), 10);
913 if (DO_OBJ) {
914 write_obj_mesh(out, "cubecubesubdivdiff");
915 }
916}
917
918TEST(boolean_polymesh, CubePlane)
919{
920 const char *spec = R"(12 7
921 -2 -2 0
922 2 -2 0
923 -2 2 0
924 2 2 0
925 -1 -1 -1
926 -1 -1 1
927 -1 1 -1
928 -1 1 1
929 1 -1 -1
930 1 -1 1
931 1 1 -1
932 1 1 1
933 0 1 3 2
934 4 5 7 6
935 6 7 11 10
936 10 11 9 8
937 8 9 5 4
938 6 10 8 4
939 11 7 5 9
940)";
941
942 IMeshBuilder mb(spec);
943 IMesh out = boolean_mesh(
944 mb.imesh,
945 BoolOpType::Difference,
946 2,
947 [](int t) { return t >= 1 ? 0 : 1; },
948 false,
949 false,
950 nullptr,
951 &mb.arena);
952 out.populate_vert();
953 EXPECT_EQ(out.vert_size(), 8);
954 EXPECT_EQ(out.face_size(), 6);
955 if (DO_OBJ) {
956 write_obj_mesh(out, "cubeplane");
957 }
958}
959
960} // namespace blender::meshintersect::tests
961#endif
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
Read Guarded memory(de)allocation.
static float verts[][3]
static char faces[256]
TEST(math_rotation, DefaultConstructor)