Blender V4.3
blenlib/intern/mesh_boolean.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#ifdef WITH_GMP
10
11# include <algorithm>
12# include <atomic>
13# include <fstream>
14# include <iostream>
15
16# include "BLI_array.hh"
17# include "BLI_assert.h"
18# include "BLI_delaunay_2d.hh"
19# include "BLI_hash.hh"
20# include "BLI_kdopbvh.h"
21# include "BLI_map.hh"
22# include "BLI_math_boolean.hh"
23# include "BLI_math_geom.h"
24# include "BLI_math_mpq.hh"
25# include "BLI_math_vector.hh"
27# include "BLI_mesh_intersect.hh"
28# include "BLI_set.hh"
29# include "BLI_span.hh"
30# include "BLI_stack.hh"
31# include "BLI_task.hh"
32# include "BLI_vector.hh"
33
34# include "BLI_mesh_boolean.hh"
35
36# ifdef WITH_TBB
37# include <tbb/parallel_reduce.h>
38# include <tbb/spin_mutex.h>
39# endif
40
41// # define PERFDEBUG
42
43namespace blender::meshintersect {
44
50class Edge {
51 const Vert *v_[2] = {nullptr, nullptr};
52
53 public:
54 Edge() = default;
55 Edge(const Vert *v0, const Vert *v1)
56 {
57 if (v0->id <= v1->id) {
58 v_[0] = v0;
59 v_[1] = v1;
60 }
61 else {
62 v_[0] = v1;
63 v_[1] = v0;
64 }
65 }
66
67 const Vert *v0() const
68 {
69 return v_[0];
70 }
71
72 const Vert *v1() const
73 {
74 return v_[1];
75 }
76
77 const Vert *operator[](int i) const
78 {
79 return v_[i];
80 }
81
82 bool operator==(Edge other) const
83 {
84 return v_[0]->id == other.v_[0]->id && v_[1]->id == other.v_[1]->id;
85 }
86
87 uint64_t hash() const
88 {
89 return get_default_hash(v_[0]->id, v_[1]->id);
90 }
91};
92
93static std::ostream &operator<<(std::ostream &os, const Edge &e)
94{
95 if (e.v0() == nullptr) {
96 BLI_assert(e.v1() == nullptr);
97 os << "(null,null)";
98 }
99 else {
100 os << "(" << e.v0() << "," << e.v1() << ")";
101 }
102 return os;
103}
104
105static std::ostream &operator<<(std::ostream &os, const Span<int> a)
106{
107 for (int i : a.index_range()) {
108 os << a[i];
109 if (i != a.size() - 1) {
110 os << " ";
111 }
112 }
113 return os;
114}
115
116static std::ostream &operator<<(std::ostream &os, const Array<int> &iarr)
117{
118 os << Span<int>(iarr);
119 return os;
120}
121
123class TriMeshTopology : NonCopyable {
125 Map<Edge, Vector<int> *> edge_tri_;
128
129 public:
130 TriMeshTopology(const IMesh &tm);
131 ~TriMeshTopology();
132
133 /* If e is manifold, return index of the other triangle (not t) that has it.
134 * Else return NO_INDEX. */
135 int other_tri_if_manifold(Edge e, int t) const
136 {
137 const auto *p = edge_tri_.lookup_ptr(e);
138 if (p != nullptr && (*p)->size() == 2) {
139 return ((**p)[0] == t) ? (**p)[1] : (**p)[0];
140 }
141 return NO_INDEX;
142 }
143
144 /* Which triangles share edge e (in either orientation)? */
145 const Vector<int> *edge_tris(Edge e) const
146 {
147 return edge_tri_.lookup_default(e, nullptr);
148 }
149
150 /* Which edges are incident on the given vertex?
151 * We assume v has some incident edges. */
152 Span<Edge> vert_edges(const Vert *v) const
153 {
154 return vert_edges_.lookup(v);
155 }
156
157 Map<Edge, Vector<int> *>::ItemIterator edge_tri_map_items() const
158 {
159 return edge_tri_.items();
160 }
161};
162
163TriMeshTopology::TriMeshTopology(const IMesh &tm)
164{
165 const int dbg_level = 0;
166 if (dbg_level > 0) {
167 std::cout << "TRIMESHTOPOLOGY CONSTRUCTION\n";
168 }
169 /* If everything were manifold, `F+V-E=2` and `E=3F/2`.
170 * So an likely overestimate, allowing for non-manifoldness, is `E=2F` and `V=F`. */
171 const int estimate_num_edges = 2 * tm.face_size();
172 const int estimate_verts_num = tm.face_size();
173 edge_tri_.reserve(estimate_num_edges);
174 vert_edges_.reserve(estimate_verts_num);
175 for (int t : tm.face_index_range()) {
176 const Face &tri = *tm.face(t);
177 BLI_assert(tri.is_tri());
178 for (int i = 0; i < 3; ++i) {
179 const Vert *v = tri[i];
180 const Vert *vnext = tri[(i + 1) % 3];
181 Edge e(v, vnext);
182 Vector<Edge> *edges = vert_edges_.lookup_ptr(v);
183 if (edges == nullptr) {
184 vert_edges_.add_new(v, Vector<Edge>());
185 edges = vert_edges_.lookup_ptr(v);
186 BLI_assert(edges != nullptr);
187 }
188 edges->append_non_duplicates(e);
189
190 auto *p = edge_tri_.lookup_ptr(Edge(v, vnext));
191 if (p == nullptr) {
192 edge_tri_.add_new(e, new Vector<int>{t});
193 }
194 else {
195 (*p)->append_non_duplicates(t);
196 }
197 }
198 }
199 /* Debugging. */
200 if (dbg_level > 0) {
201 std::cout << "After TriMeshTopology construction\n";
202 for (auto item : edge_tri_.items()) {
203 std::cout << "tris for edge " << item.key << ": " << *item.value << "\n";
204 constexpr bool print_stats = false;
205 if (print_stats) {
206 edge_tri_.print_stats("");
207 }
208 }
209 for (auto item : vert_edges_.items()) {
210 std::cout << "edges for vert " << item.key << ":\n";
211 for (const Edge &e : item.value) {
212 std::cout << " " << e << "\n";
213 }
214 std::cout << "\n";
215 }
216 }
217}
218
219TriMeshTopology::~TriMeshTopology()
220{
221 Vector<Vector<int> *> values;
222
223 /* Deconstructing is faster in parallel, so it is worth building an array of things to delete. */
224 for (auto *item : edge_tri_.values()) {
225 values.append(item);
226 }
227
228 threading::parallel_for(values.index_range(), 256, [&](IndexRange range) {
229 for (int i : range) {
230 delete values[i];
231 }
232 });
233}
234
236class Patch {
237 Vector<int> tri_; /* Indices of triangles in the Patch. */
238
239 public:
240 Patch() = default;
241
242 void add_tri(int t)
243 {
244 tri_.append(t);
245 }
246
247 int tot_tri() const
248 {
249 return tri_.size();
250 }
251
252 int tri(int i) const
253 {
254 return tri_[i];
255 }
256
257 IndexRange tri_range() const
258 {
259 return IndexRange(tri_.size());
260 }
261
262 Span<int> tris() const
263 {
264 return Span<int>(tri_);
265 }
266
267 int cell_above{NO_INDEX};
268 int cell_below{NO_INDEX};
269 int component{NO_INDEX};
270};
271
272static std::ostream &operator<<(std::ostream &os, const Patch &patch)
273{
274 os << "Patch " << patch.tris();
275 if (patch.cell_above != NO_INDEX) {
276 os << " cell_above=" << patch.cell_above;
277 }
278 else {
279 os << " cell_above not set";
280 }
281 if (patch.cell_below != NO_INDEX) {
282 os << " cell_below=" << patch.cell_below;
283 }
284 else {
285 os << " cell_below not set";
286 }
287 return os;
288}
289
290class PatchesInfo {
292 Vector<Patch> patch_;
294 Array<int> tri_patch_;
296 Map<std::pair<int, int>, Edge> pp_edge_;
297
298 public:
299 explicit PatchesInfo(int ntri)
300 {
301 constexpr int max_expected_patch_patch_incidences = 100;
302 tri_patch_ = Array<int>(ntri, NO_INDEX);
303 pp_edge_.reserve(max_expected_patch_patch_incidences);
304 }
305
306 int tri_patch(int t) const
307 {
308 return tri_patch_[t];
309 }
310
311 int add_patch()
312 {
313 int patch_index = patch_.append_and_get_index(Patch());
314 return patch_index;
315 }
316
317 void grow_patch(int patch_index, int t)
318 {
319 tri_patch_[t] = patch_index;
320 patch_[patch_index].add_tri(t);
321 }
322
323 bool tri_is_assigned(int t) const
324 {
325 return tri_patch_[t] != NO_INDEX;
326 }
327
328 const Patch &patch(int patch_index) const
329 {
330 return patch_[patch_index];
331 }
332
333 Patch &patch(int patch_index)
334 {
335 return patch_[patch_index];
336 }
337
338 int tot_patch() const
339 {
340 return patch_.size();
341 }
342
343 IndexRange index_range() const
344 {
345 return IndexRange(patch_.size());
346 }
347
348 const Patch *begin() const
349 {
350 return patch_.begin();
351 }
352
353 const Patch *end() const
354 {
355 return patch_.end();
356 }
357
358 Patch *begin()
359 {
360 return patch_.begin();
361 }
362
363 Patch *end()
364 {
365 return patch_.end();
366 }
367
368 void add_new_patch_patch_edge(int p1, int p2, Edge e)
369 {
370 pp_edge_.add_new(std::pair<int, int>(p1, p2), e);
371 pp_edge_.add_new(std::pair<int, int>(p2, p1), e);
372 }
373
374 Edge patch_patch_edge(int p1, int p2)
375 {
376 return pp_edge_.lookup_default(std::pair<int, int>(p1, p2), Edge());
377 }
378
379 const Map<std::pair<int, int>, Edge> &patch_patch_edge_map()
380 {
381 return pp_edge_;
382 }
383};
384
385static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding);
386
392class Cell {
393 Set<int> patches_;
394 Array<int> winding_;
395 int merged_to_{NO_INDEX};
396 bool winding_assigned_{false};
397 /* in_output_volume_ will be true when this cell should be in the output volume. */
398 bool in_output_volume_{false};
399 /* zero_volume_ will be true when this is a zero-volume cell (inside a stack of identical
400 * triangles). */
401 bool zero_volume_{false};
402
403 public:
404 Cell() = default;
405
406 void add_patch(int p)
407 {
408 patches_.add(p);
409 zero_volume_ = false; /* If it was true before, it no longer is. */
410 }
411
412 const Set<int> &patches() const
413 {
414 return patches_;
415 }
416
418 int patch_other(int p) const
419 {
420 if (patches_.size() != 2) {
421 return NO_INDEX;
422 }
423 for (int pother : patches_) {
424 if (pother != p) {
425 return pother;
426 }
427 }
428 return NO_INDEX;
429 }
430
431 Span<int> winding() const
432 {
433 return Span<int>(winding_);
434 }
435
436 void init_winding(int winding_len)
437 {
438 winding_ = Array<int>(winding_len);
439 }
440
441 void seed_ambient_winding()
442 {
443 winding_.fill(0);
444 winding_assigned_ = true;
445 }
446
447 void set_winding_and_in_output_volume(const Cell &from_cell,
448 int shape,
449 int delta,
450 BoolOpType bool_optype)
451 {
452 std::copy(from_cell.winding().begin(), from_cell.winding().end(), winding_.begin());
453 if (shape >= 0) {
454 winding_[shape] += delta;
455 }
456 winding_assigned_ = true;
457 in_output_volume_ = apply_bool_op(bool_optype, winding_);
458 }
459
460 bool in_output_volume() const
461 {
462 return in_output_volume_;
463 }
464
465 bool winding_assigned() const
466 {
467 return winding_assigned_;
468 }
469
470 bool zero_volume() const
471 {
472 return zero_volume_;
473 }
474
475 int merged_to() const
476 {
477 return merged_to_;
478 }
479
480 void set_merged_to(int c)
481 {
482 merged_to_ = c;
483 }
484
489 void check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh);
490};
491
492static std::ostream &operator<<(std::ostream &os, const Cell &cell)
493{
494 os << "Cell patches";
495 for (int p : cell.patches()) {
496 std::cout << " " << p;
497 }
498 if (cell.winding().size() > 0) {
499 os << " winding=" << cell.winding();
500 os << " in_output_volume=" << cell.in_output_volume();
501 }
502 os << " zv=" << cell.zero_volume();
503 std::cout << "\n";
504 return os;
505}
506
507static bool tris_have_same_verts(const IMesh &mesh, int t1, int t2)
508{
509 const Face &tri1 = *mesh.face(t1);
510 const Face &tri2 = *mesh.face(t2);
511 BLI_assert(tri1.size() == 3 && tri2.size() == 3);
512 if (tri1.vert[0] == tri2.vert[0]) {
513 return ((tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[2]) ||
514 (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[1]));
515 }
516 if (tri1.vert[0] == tri2.vert[1]) {
517 return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[2]) ||
518 (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[0]));
519 }
520 if (tri1.vert[0] == tri2.vert[2]) {
521 return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[1]) ||
522 (tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[0]));
523 }
524 return false;
525}
526
532void Cell::check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh)
533{
534 if (patches_.size() == 2) {
535 int p1_index = NO_INDEX;
536 int p2_index = NO_INDEX;
537 for (int p : patches_) {
538 if (p1_index == NO_INDEX) {
539 p1_index = p;
540 }
541 else {
542 p2_index = p;
543 }
544 }
545 BLI_assert(p1_index != NO_INDEX && p2_index != NO_INDEX);
546 const Patch &p1 = pinfo.patch(p1_index);
547 const Patch &p2 = pinfo.patch(p2_index);
548 if (p1.tot_tri() == 1 && p2.tot_tri() == 1) {
549 if (tris_have_same_verts(mesh, p1.tri(0), p2.tri(0))) {
550 zero_volume_ = true;
551 }
552 }
553 }
554}
555
556/* Information about all the Cells. */
557class CellsInfo {
558 Vector<Cell> cell_;
559
560 public:
561 CellsInfo() = default;
562
563 int add_cell()
564 {
565 int index = cell_.append_and_get_index(Cell());
566 return index;
567 }
568
569 Cell &cell(int c)
570 {
571 return cell_[c];
572 }
573
574 const Cell &cell(int c) const
575 {
576 return cell_[c];
577 }
578
579 int tot_cell() const
580 {
581 return cell_.size();
582 }
583
584 IndexRange index_range() const
585 {
586 return cell_.index_range();
587 }
588
589 const Cell *begin() const
590 {
591 return cell_.begin();
592 }
593
594 const Cell *end() const
595 {
596 return cell_.end();
597 }
598
599 Cell *begin()
600 {
601 return cell_.begin();
602 }
603
604 Cell *end()
605 {
606 return cell_.end();
607 }
608
609 void init_windings(int winding_len)
610 {
611 for (Cell &cell : cell_) {
612 cell.init_winding(winding_len);
613 }
614 }
615};
616
620static void write_obj_cell_patch(const IMesh &m,
621 const CellsInfo &cinfo,
622 const PatchesInfo &pinfo,
623 bool cells_only,
624 const std::string &name)
625{
626 /* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library.
627 * This is just for developer debugging anyway,
628 * and should never be called in production Blender. */
629# ifdef _WIN_32
630 const char *objdir = BLI_getenv("HOME");
631# else
632 const char *objdir = "/tmp/";
633# endif
634
635 std::string fname = std::string(objdir) + name + std::string("_cellpatch.obj");
636 std::ofstream f;
637 f.open(fname);
638 if (!f) {
639 std::cout << "Could not open file " << fname << "\n";
640 return;
641 }
642
643 /* Copy IMesh so can populate verts. */
644 IMesh mm = m;
645 mm.populate_vert();
646 f << "o cellpatch\n";
647 for (const Vert *v : mm.vertices()) {
648 const double3 dv = v->co;
649 f << "v " << dv[0] << " " << dv[1] << " " << dv[2] << "\n";
650 }
651 if (!cells_only) {
652 for (int p : pinfo.index_range()) {
653 f << "g patch" << p << "\n";
654 const Patch &patch = pinfo.patch(p);
655 for (int t : patch.tris()) {
656 const Face &tri = *mm.face(t);
657 f << "f ";
658 for (const Vert *v : tri) {
659 f << mm.lookup_vert(v) + 1 << " ";
660 }
661 f << "\n";
662 }
663 }
664 }
665 for (int c : cinfo.index_range()) {
666 f << "g cell" << c << "\n";
667 const Cell &cell = cinfo.cell(c);
668 for (int p : cell.patches()) {
669 const Patch &patch = pinfo.patch(p);
670 for (int t : patch.tris()) {
671 const Face &tri = *mm.face(t);
672 f << "f ";
673 for (const Vert *v : tri) {
674 f << mm.lookup_vert(v) + 1 << " ";
675 }
676 f << "\n";
677 }
678 }
679 }
680 f.close();
681}
682
683static void merge_cells(int merge_to, int merge_from, CellsInfo &cinfo, PatchesInfo &pinfo)
684{
685 if (merge_to == merge_from) {
686 return;
687 }
688 Cell &merge_from_cell = cinfo.cell(merge_from);
689 Cell &merge_to_cell = cinfo.cell(merge_to);
690 int final_merge_to = merge_to;
691 while (merge_to_cell.merged_to() != NO_INDEX) {
692 final_merge_to = merge_to_cell.merged_to();
693 merge_to_cell = cinfo.cell(final_merge_to);
694 }
695 for (int cell_p : merge_from_cell.patches()) {
696 merge_to_cell.add_patch(cell_p);
697 Patch &patch = pinfo.patch(cell_p);
698 if (patch.cell_above == merge_from) {
699 patch.cell_above = merge_to;
700 }
701 if (patch.cell_below == merge_from) {
702 patch.cell_below = merge_to;
703 }
704 }
705 merge_from_cell.set_merged_to(final_merge_to);
706}
707
711static PatchesInfo find_patches(const IMesh &tm, const TriMeshTopology &tmtopo)
712{
713 const int dbg_level = 0;
714 if (dbg_level > 0) {
715 std::cout << "\nFIND_PATCHES\n";
716 }
717 int ntri = tm.face_size();
718 PatchesInfo pinfo(ntri);
719 /* Algorithm: Grow patches across manifold edges as long as there are unassigned triangles. */
720 Stack<int> cur_patch_grow;
721
722 /* Create an Array containing indices of adjacent faces. */
723 Array<std::array<int, 3>> t_others(tm.face_size());
724 threading::parallel_for(tm.face_index_range(), 2048, [&](IndexRange range) {
725 for (int t : range) {
726 const Face &tri = *tm.face(t);
727 for (int i = 0; i < 3; ++i) {
728 Edge e(tri[i], tri[(i + 1) % 3]);
729 t_others[t][i] = tmtopo.other_tri_if_manifold(e, t);
730 }
731 }
732 });
733 for (int t : tm.face_index_range()) {
734 if (pinfo.tri_patch(t) == -1) {
735 cur_patch_grow.push(t);
736 int cur_patch_index = pinfo.add_patch();
737 while (!cur_patch_grow.is_empty()) {
738 int tcand = cur_patch_grow.pop();
739 if (dbg_level > 1) {
740 std::cout << "pop tcand = " << tcand << "; assigned = " << pinfo.tri_is_assigned(tcand)
741 << "\n";
742 }
743 if (pinfo.tri_is_assigned(tcand)) {
744 continue;
745 }
746 if (dbg_level > 1) {
747 std::cout << "grow patch from seed tcand=" << tcand << "\n";
748 }
749 pinfo.grow_patch(cur_patch_index, tcand);
750 const Face &tri = *tm.face(tcand);
751 for (int i = 0; i < 3; ++i) {
752 Edge e(tri[i], tri[(i + 1) % 3]);
753 int t_other = t_others[tcand][i];
754 if (dbg_level > 1) {
755 std::cout << " edge " << e << " generates t_other=" << t_other << "\n";
756 }
757 if (t_other != NO_INDEX) {
758 if (!pinfo.tri_is_assigned(t_other)) {
759 if (dbg_level > 1) {
760 std::cout << " push t_other = " << t_other << "\n";
761 }
762 cur_patch_grow.push(t_other);
763 }
764 }
765 else {
766 /* e is non-manifold. Set any patch-patch incidences we can. */
767 if (dbg_level > 1) {
768 std::cout << " e non-manifold case\n";
769 }
770 const Vector<int> *etris = tmtopo.edge_tris(e);
771 if (etris != nullptr) {
772 for (int i : etris->index_range()) {
773 int t_other = (*etris)[i];
774 if (t_other != tcand && pinfo.tri_is_assigned(t_other)) {
775 int p_other = pinfo.tri_patch(t_other);
776 if (p_other == cur_patch_index) {
777 continue;
778 }
779 if (pinfo.patch_patch_edge(cur_patch_index, p_other).v0() == nullptr) {
780 pinfo.add_new_patch_patch_edge(cur_patch_index, p_other, e);
781 if (dbg_level > 1) {
782 std::cout << "added patch_patch_edge (" << cur_patch_index << "," << p_other
783 << ") = " << e << "\n";
784 }
785 }
786 }
787 }
788 }
789 }
790 }
791 }
792 }
793 }
794 if (dbg_level > 0) {
795 std::cout << "\nafter FIND_PATCHES: found " << pinfo.tot_patch() << " patches\n";
796 for (int p : pinfo.index_range()) {
797 std::cout << p << ": " << pinfo.patch(p) << "\n";
798 }
799 if (dbg_level > 1) {
800 std::cout << "\ntriangle map\n";
801 for (int t : tm.face_index_range()) {
802 std::cout << t << ": " << tm.face(t) << " patch " << pinfo.tri_patch(t) << "\n";
803 }
804 }
805 std::cout << "\npatch-patch incidences\n";
806 for (int p1 : pinfo.index_range()) {
807 for (int p2 : pinfo.index_range()) {
808 Edge e = pinfo.patch_patch_edge(p1, p2);
809 if (e.v0() != nullptr) {
810 std::cout << "p" << p1 << " and p" << p2 << " share edge " << e << "\n";
811 }
812 }
813 }
814 }
815 return pinfo;
816}
817
824static const Vert *find_flap_vert(const Face &tri, const Edge e, bool *r_rev)
825{
826 *r_rev = false;
827 const Vert *flapv;
828 if (tri[0] == e.v0()) {
829 if (tri[1] == e.v1()) {
830 *r_rev = false;
831 flapv = tri[2];
832 }
833 else {
834 if (tri[2] != e.v1()) {
835 return nullptr;
836 }
837 *r_rev = true;
838 flapv = tri[1];
839 }
840 }
841 else if (tri[1] == e.v0()) {
842 if (tri[2] == e.v1()) {
843 *r_rev = false;
844 flapv = tri[0];
845 }
846 else {
847 if (tri[0] != e.v1()) {
848 return nullptr;
849 }
850 *r_rev = true;
851 flapv = tri[2];
852 }
853 }
854 else {
855 if (tri[2] != e.v0()) {
856 return nullptr;
857 }
858 if (tri[0] == e.v1()) {
859 *r_rev = false;
860 flapv = tri[1];
861 }
862 else {
863 if (tri[1] != e.v1()) {
864 return nullptr;
865 }
866 *r_rev = true;
867 flapv = tri[0];
868 }
869 }
870 return flapv;
871}
872
887static int sort_tris_class(const Face &tri, const Face &tri0, const Edge e)
888{
889 const int dbg_level = 0;
890 if (dbg_level > 0) {
891 std::cout << "classify e = " << e << "\n";
892 }
893 mpq3 a0 = tri0[0]->co_exact;
894 mpq3 a1 = tri0[1]->co_exact;
895 mpq3 a2 = tri0[2]->co_exact;
896 bool rev;
897 bool rev0;
898 const Vert *flapv0 = find_flap_vert(tri0, e, &rev0);
899 const Vert *flapv = find_flap_vert(tri, e, &rev);
900 if (dbg_level > 0) {
901 std::cout << " t0 = " << tri0[0] << " " << tri0[1] << " " << tri0[2];
902 std::cout << " rev0 = " << rev0 << " flapv0 = " << flapv0 << "\n";
903 std::cout << " t = " << tri[0] << " " << tri[1] << " " << tri[2];
904 std::cout << " rev = " << rev << " flapv = " << flapv << "\n";
905 }
906 BLI_assert(flapv != nullptr && flapv0 != nullptr);
907 const mpq3 flap = flapv->co_exact;
908 /* orient will be positive if flap is below oriented plane of a0,a1,a2. */
909 int orient = orient3d(a0, a1, a2, flap);
910 int ans;
911 if (orient > 0) {
912 ans = rev0 ? 4 : 3;
913 }
914 else if (orient < 0) {
915 ans = rev0 ? 3 : 4;
916 }
917 else {
918 ans = flapv == flapv0 ? 1 : 2;
919 }
920 if (dbg_level > 0) {
921 std::cout << " orient = " << orient << " ans = " << ans << "\n";
922 }
923 return ans;
924}
925
926constexpr int EXTRA_TRI_INDEX = INT_MAX;
927
934static void sort_by_signed_triangle_index(Vector<int> &g,
935 const Edge e,
936 const IMesh &tm,
937 const Face *extra_tri)
938{
939 Array<int> signed_g(g.size());
940 for (int i : g.index_range()) {
941 const Face &tri = g[i] == EXTRA_TRI_INDEX ? *extra_tri : *tm.face(g[i]);
942 bool rev;
943 find_flap_vert(tri, e, &rev);
944 signed_g[i] = rev ? -g[i] : g[i];
945 }
946 std::sort(signed_g.begin(), signed_g.end());
947
948 for (int i : g.index_range()) {
949 g[i] = abs(signed_g[i]);
950 }
951}
952
967static Array<int> sort_tris_around_edge(
968 const IMesh &tm, const Edge e, const Span<int> tris, const int t0, const Face *extra_tri)
969{
970 /* Divide and conquer, quick-sort-like sort.
971 * Pick a triangle t0, then partition into groups:
972 * (1) co-planar with t0 and on same side of e
973 * (2) co-planar with t0 and on opposite side of e
974 * (3) below plane of t0
975 * (4) above plane of t0
976 * Each group is sorted and then the sorts are merged to give the answer.
977 * We don't expect the input array to be very large - should typically
978 * be only 3 or 4 - so OK to make copies of arrays instead of swapping
979 * around in a single array. */
980 const int dbg_level = 0;
981 if (tris.is_empty()) {
982 return Array<int>();
983 }
984 if (dbg_level > 0) {
985 if (t0 == tris[0]) {
986 std::cout << "\n";
987 }
988 std::cout << "sort_tris_around_edge " << e << "\n";
989 std::cout << "tris = " << tris << "\n";
990 std::cout << "t0 = " << t0 << "\n";
991 }
992 Vector<int> g1{tris[0]};
993 Vector<int> g2;
994 Vector<int> g3;
995 Vector<int> g4;
996 std::array<Vector<int> *, 4> groups = {&g1, &g2, &g3, &g4};
997 const Face &triref = *tm.face(tris[0]);
998 for (int i : tris.index_range()) {
999 if (i == 0) {
1000 continue;
1001 }
1002 int t = tris[i];
1003 BLI_assert(t < tm.face_size() || (t == EXTRA_TRI_INDEX && extra_tri != nullptr));
1004 const Face &tri = (t == EXTRA_TRI_INDEX) ? *extra_tri : *tm.face(t);
1005 if (dbg_level > 2) {
1006 std::cout << "classifying tri " << t << " with respect to " << tris[0] << "\n";
1007 }
1008 int group_num = sort_tris_class(tri, triref, e);
1009 if (dbg_level > 2) {
1010 std::cout << " classify result : " << group_num << "\n";
1011 }
1012 groups[group_num - 1]->append(t);
1013 }
1014 if (dbg_level > 1) {
1015 std::cout << "g1 = " << g1 << "\n";
1016 std::cout << "g2 = " << g2 << "\n";
1017 std::cout << "g3 = " << g3 << "\n";
1018 std::cout << "g4 = " << g4 << "\n";
1019 }
1020 if (g1.size() > 1) {
1021 sort_by_signed_triangle_index(g1, e, tm, extra_tri);
1022 if (dbg_level > 1) {
1023 std::cout << "g1 sorted: " << g1 << "\n";
1024 }
1025 }
1026 if (g2.size() > 1) {
1027 sort_by_signed_triangle_index(g2, e, tm, extra_tri);
1028 if (dbg_level > 1) {
1029 std::cout << "g2 sorted: " << g2 << "\n";
1030 }
1031 }
1032 if (g3.size() > 1) {
1033 Array<int> g3sorted = sort_tris_around_edge(tm, e, g3, t0, extra_tri);
1034 std::copy(g3sorted.begin(), g3sorted.end(), g3.begin());
1035 if (dbg_level > 1) {
1036 std::cout << "g3 sorted: " << g3 << "\n";
1037 }
1038 }
1039 if (g4.size() > 1) {
1040 Array<int> g4sorted = sort_tris_around_edge(tm, e, g4, t0, extra_tri);
1041 std::copy(g4sorted.begin(), g4sorted.end(), g4.begin());
1042 if (dbg_level > 1) {
1043 std::cout << "g4 sorted: " << g4 << "\n";
1044 }
1045 }
1046 int group_tot_size = g1.size() + g2.size() + g3.size() + g4.size();
1047 Array<int> ans(group_tot_size);
1048 int *p = ans.begin();
1049 if (tris[0] == t0) {
1050 p = std::copy(g1.begin(), g1.end(), p);
1051 p = std::copy(g4.begin(), g4.end(), p);
1052 p = std::copy(g2.begin(), g2.end(), p);
1053 std::copy(g3.begin(), g3.end(), p);
1054 }
1055 else {
1056 p = std::copy(g3.begin(), g3.end(), p);
1057 p = std::copy(g1.begin(), g1.end(), p);
1058 p = std::copy(g4.begin(), g4.end(), p);
1059 std::copy(g2.begin(), g2.end(), p);
1060 }
1061 if (dbg_level > 0) {
1062 std::cout << "sorted tris = " << ans << "\n";
1063 }
1064 return ans;
1065}
1066
1073static void find_cells_from_edge(const IMesh &tm,
1074 const TriMeshTopology &tmtopo,
1075 PatchesInfo &pinfo,
1076 CellsInfo &cinfo,
1077 const Edge e)
1078{
1079 const int dbg_level = 0;
1080 if (dbg_level > 0) {
1081 std::cout << "FIND_CELLS_FROM_EDGE " << e << "\n";
1082 }
1083 const Vector<int> *edge_tris = tmtopo.edge_tris(e);
1084 BLI_assert(edge_tris != nullptr);
1085 Array<int> sorted_tris = sort_tris_around_edge(
1086 tm, e, Span<int>(*edge_tris), (*edge_tris)[0], nullptr);
1087
1088 int n_edge_tris = edge_tris->size();
1089 Array<int> edge_patches(n_edge_tris);
1090 for (int i = 0; i < n_edge_tris; ++i) {
1091 edge_patches[i] = pinfo.tri_patch(sorted_tris[i]);
1092 if (dbg_level > 1) {
1093 std::cout << "edge_patches[" << i << "] = " << edge_patches[i] << "\n";
1094 }
1095 }
1096 for (int i = 0; i < n_edge_tris; ++i) {
1097 int inext = (i + 1) % n_edge_tris;
1098 int r_index = edge_patches[i];
1099 int rnext_index = edge_patches[inext];
1100 Patch &r = pinfo.patch(r_index);
1101 Patch &rnext = pinfo.patch(rnext_index);
1102 bool r_flipped;
1103 bool rnext_flipped;
1104 find_flap_vert(*tm.face(sorted_tris[i]), e, &r_flipped);
1105 find_flap_vert(*tm.face(sorted_tris[inext]), e, &rnext_flipped);
1106 int *r_follow_cell = r_flipped ? &r.cell_below : &r.cell_above;
1107 int *rnext_prev_cell = rnext_flipped ? &rnext.cell_above : &rnext.cell_below;
1108 if (dbg_level > 0) {
1109 std::cout << "process patch pair " << r_index << " " << rnext_index << "\n";
1110 std::cout << " r_flipped = " << r_flipped << " rnext_flipped = " << rnext_flipped << "\n";
1111 std::cout << " r_follow_cell (" << (r_flipped ? "below" : "above")
1112 << ") = " << *r_follow_cell << "\n";
1113 std::cout << " rnext_prev_cell (" << (rnext_flipped ? "above" : "below")
1114 << ") = " << *rnext_prev_cell << "\n";
1115 }
1116 if (*r_follow_cell == NO_INDEX && *rnext_prev_cell == NO_INDEX) {
1117 /* Neither is assigned: make a new cell. */
1118 int c = cinfo.add_cell();
1119 *r_follow_cell = c;
1120 *rnext_prev_cell = c;
1121 Cell &cell = cinfo.cell(c);
1122 cell.add_patch(r_index);
1123 cell.add_patch(rnext_index);
1124 cell.check_for_zero_volume(pinfo, tm);
1125 if (dbg_level > 0) {
1126 std::cout << " made new cell " << c << "\n";
1127 std::cout << " p" << r_index << "." << (r_flipped ? "cell_below" : "cell_above") << " = c"
1128 << c << "\n";
1129 std::cout << " p" << rnext_index << "." << (rnext_flipped ? "cell_above" : "cell_below")
1130 << " = c" << c << "\n";
1131 }
1132 }
1133 else if (*r_follow_cell != NO_INDEX && *rnext_prev_cell == NO_INDEX) {
1134 int c = *r_follow_cell;
1135 *rnext_prev_cell = c;
1136 Cell &cell = cinfo.cell(c);
1137 cell.add_patch(rnext_index);
1138 cell.check_for_zero_volume(pinfo, tm);
1139 if (dbg_level > 0) {
1140 std::cout << " reuse r_follow: p" << rnext_index << "."
1141 << (rnext_flipped ? "cell_above" : "cell_below") << " = c" << c << "\n";
1142 }
1143 }
1144 else if (*r_follow_cell == NO_INDEX && *rnext_prev_cell != NO_INDEX) {
1145 int c = *rnext_prev_cell;
1146 *r_follow_cell = c;
1147 Cell &cell = cinfo.cell(c);
1148 cell.add_patch(r_index);
1149 cell.check_for_zero_volume(pinfo, tm);
1150 if (dbg_level > 0) {
1151 std::cout << " reuse rnext prev: rprev_p" << r_index << "."
1152 << (r_flipped ? "cell_below" : "cell_above") << " = c" << c << "\n";
1153 }
1154 }
1155 else {
1156 if (*r_follow_cell != *rnext_prev_cell) {
1157 int follow_cell_num_patches = cinfo.cell(*r_follow_cell).patches().size();
1158 int prev_cell_num_patches = cinfo.cell(*rnext_prev_cell).patches().size();
1159 if (follow_cell_num_patches >= prev_cell_num_patches) {
1160 if (dbg_level > 0) {
1161 std::cout << " merge cell " << *rnext_prev_cell << " into cell " << *r_follow_cell
1162 << "\n";
1163 }
1164 merge_cells(*r_follow_cell, *rnext_prev_cell, cinfo, pinfo);
1165 }
1166 }
1167 else {
1168 if (dbg_level > 0) {
1169 std::cout << " merge cell " << *r_follow_cell << " into cell " << *rnext_prev_cell
1170 << "\n";
1171 }
1172 merge_cells(*rnext_prev_cell, *r_follow_cell, cinfo, pinfo);
1173 }
1174 }
1175 }
1176}
1177
1182static CellsInfo find_cells(const IMesh &tm, const TriMeshTopology &tmtopo, PatchesInfo &pinfo)
1183{
1184 const int dbg_level = 0;
1185 if (dbg_level > 0) {
1186 std::cout << "\nFIND_CELLS\n";
1187 }
1188 CellsInfo cinfo;
1189 /* For each unique edge shared between patch pairs, process it. */
1190 Set<Edge> processed_edges;
1191 for (const auto item : pinfo.patch_patch_edge_map().items()) {
1192 int p = item.key.first;
1193 int q = item.key.second;
1194 if (p < q) {
1195 const Edge &e = item.value;
1196 if (!processed_edges.contains(e)) {
1197 processed_edges.add_new(e);
1198 find_cells_from_edge(tm, tmtopo, pinfo, cinfo, e);
1199 }
1200 }
1201 }
1202 /* Some patches may have no cells at this point. These are either:
1203 * (a) a closed manifold patch only incident on itself (sphere, torus, klein bottle, etc.).
1204 * (b) an open manifold patch only incident on itself (has non-manifold boundaries).
1205 * Make above and below cells for these patches. This will create a disconnected patch-cell
1206 * bipartite graph, which will have to be fixed later. */
1207 for (int p : pinfo.index_range()) {
1208 Patch &patch = pinfo.patch(p);
1209 if (patch.cell_above == NO_INDEX) {
1210 int c = cinfo.add_cell();
1211 patch.cell_above = c;
1212 Cell &cell = cinfo.cell(c);
1213 cell.add_patch(p);
1214 }
1215 if (patch.cell_below == NO_INDEX) {
1216 int c = cinfo.add_cell();
1217 patch.cell_below = c;
1218 Cell &cell = cinfo.cell(c);
1219 cell.add_patch(p);
1220 }
1221 }
1222 if (dbg_level > 0) {
1223 std::cout << "\nFIND_CELLS found " << cinfo.tot_cell() << " cells\nCells\n";
1224 for (int i : cinfo.index_range()) {
1225 std::cout << i << ": " << cinfo.cell(i) << "\n";
1226 }
1227 std::cout << "Patches\n";
1228 for (int i : pinfo.index_range()) {
1229 std::cout << i << ": " << pinfo.patch(i) << "\n";
1230 }
1231 if (dbg_level > 1) {
1232 write_obj_cell_patch(tm, cinfo, pinfo, false, "postfindcells");
1233 }
1234 }
1235 return cinfo;
1236}
1237
1243static Vector<Vector<int>> find_patch_components(const CellsInfo &cinfo, PatchesInfo &pinfo)
1244{
1245 constexpr int dbg_level = 0;
1246 if (dbg_level > 0) {
1247 std::cout << "FIND_PATCH_COMPONENTS\n";
1248 }
1249 if (pinfo.tot_patch() == 0) {
1250 return Vector<Vector<int>>();
1251 }
1252 int current_component = 0;
1253 Array<bool> cell_processed(cinfo.tot_cell(), false);
1254 Stack<int> stack; /* Patch indices to visit. */
1255 Vector<Vector<int>> ans;
1256 for (int pstart : pinfo.index_range()) {
1257 Patch &patch_pstart = pinfo.patch(pstart);
1258 if (patch_pstart.component != NO_INDEX) {
1259 continue;
1260 }
1261 ans.append(Vector<int>());
1262 ans[current_component].append(pstart);
1263 stack.push(pstart);
1264 patch_pstart.component = current_component;
1265 while (!stack.is_empty()) {
1266 int p = stack.pop();
1267 Patch &patch = pinfo.patch(p);
1268 BLI_assert(patch.component == current_component);
1269 for (int c : {patch.cell_above, patch.cell_below}) {
1270 if (cell_processed[c]) {
1271 continue;
1272 }
1273 cell_processed[c] = true;
1274 for (int pn : cinfo.cell(c).patches()) {
1275 Patch &patch_neighbor = pinfo.patch(pn);
1276 if (patch_neighbor.component == NO_INDEX) {
1277 patch_neighbor.component = current_component;
1278 stack.push(pn);
1279 ans[current_component].append(pn);
1280 }
1281 }
1282 }
1283 }
1284 ++current_component;
1285 }
1286 if (dbg_level > 0) {
1287 std::cout << "found " << ans.size() << " components\n";
1288 for (int comp : ans.index_range()) {
1289 std::cout << comp << ": " << ans[comp] << "\n";
1290 }
1291 }
1292 return ans;
1293}
1294
1299static bool patch_cell_graph_ok(const CellsInfo &cinfo, const PatchesInfo &pinfo)
1300{
1301 for (int c : cinfo.index_range()) {
1302 const Cell &cell = cinfo.cell(c);
1303 if (cell.merged_to() != NO_INDEX) {
1304 continue;
1305 }
1306 if (cell.patches().is_empty()) {
1307 std::cout << "Patch/Cell graph disconnected at Cell " << c << " with no patches\n";
1308 return false;
1309 }
1310 for (int p : cell.patches()) {
1311 if (p >= pinfo.tot_patch()) {
1312 std::cout << "Patch/Cell graph has bad patch index at Cell " << c << "\n";
1313 return false;
1314 }
1315 }
1316 }
1317 for (int p : pinfo.index_range()) {
1318 const Patch &patch = pinfo.patch(p);
1319 if (patch.cell_above == NO_INDEX || patch.cell_below == NO_INDEX) {
1320 std::cout << "Patch/Cell graph disconnected at Patch " << p
1321 << " with one or two missing cells\n";
1322 return false;
1323 }
1324 if (patch.cell_above >= cinfo.tot_cell() || patch.cell_below >= cinfo.tot_cell()) {
1325 std::cout << "Patch/Cell graph has bad cell index at Patch " << p << "\n";
1326 return false;
1327 }
1328 }
1329 return true;
1330}
1331
1345static bool is_pwn(const IMesh &tm, const TriMeshTopology &tmtopo)
1346{
1347 constexpr int dbg_level = 0;
1348 std::atomic<bool> is_pwn = true;
1350
1351 for (auto item : tmtopo.edge_tri_map_items()) {
1352 tris.append(std::pair<Edge, Vector<int> *>(item.key, item.value));
1353 }
1354
1355 threading::parallel_for(tris.index_range(), 2048, [&](IndexRange range) {
1356 if (!is_pwn.load()) {
1357 /* Early out if mesh is already determined to be non-pwn. */
1358 return;
1359 }
1360
1361 for (int j : range) {
1362 const Edge &edge = tris[j].first;
1363 int tot_orient = 0;
1364 /* For each face t attached to edge, add +1 if the edge
1365 * is positively in t, and -1 if negatively in t. */
1366 for (int t : *tris[j].second) {
1367 const Face &face = *tm.face(t);
1368 BLI_assert(face.size() == 3);
1369 for (int i : face.index_range()) {
1370 if (face[i] == edge.v0()) {
1371 if (face[(i + 1) % 3] == edge.v1()) {
1372 ++tot_orient;
1373 }
1374 else {
1375 BLI_assert(face[(i + 3 - 1) % 3] == edge.v1());
1376 --tot_orient;
1377 }
1378 }
1379 }
1380 }
1381 if (tot_orient != 0) {
1382 if (dbg_level > 0) {
1383 std::cout << "edge causing non-pwn: " << edge << "\n";
1384 }
1385 is_pwn = false;
1386 break;
1387 }
1388 }
1389 });
1390 return is_pwn.load();
1391}
1392
1400static int find_cell_for_point_near_edge(const mpq3 &p,
1401 const Edge &e,
1402 const IMesh &tm,
1403 const TriMeshTopology &tmtopo,
1404 const PatchesInfo &pinfo,
1405 IMeshArena *arena)
1406{
1407 constexpr int dbg_level = 0;
1408 if (dbg_level > 0) {
1409 std::cout << "FIND_CELL_FOR_POINT_NEAR_EDGE, p=" << p << " e=" << e << "\n";
1410 }
1411 const Vector<int> *etris = tmtopo.edge_tris(e);
1412 const Vert *dummy_vert = arena->add_or_find_vert(p, NO_INDEX);
1413 const Face *dummy_tri = arena->add_face({e.v0(), e.v1(), dummy_vert},
1414 NO_INDEX,
1415 {NO_INDEX, NO_INDEX, NO_INDEX},
1416 {false, false, false});
1417 BLI_assert(etris != nullptr);
1418 Array<int> edge_tris(etris->size() + 1);
1419 std::copy(etris->begin(), etris->end(), edge_tris.begin());
1420 edge_tris[edge_tris.size() - 1] = EXTRA_TRI_INDEX;
1421 Array<int> sorted_tris = sort_tris_around_edge(tm, e, edge_tris, edge_tris[0], dummy_tri);
1422 if (dbg_level > 0) {
1423 std::cout << "sorted tris = " << sorted_tris << "\n";
1424 }
1425 int *p_sorted_dummy = std::find(sorted_tris.begin(), sorted_tris.end(), EXTRA_TRI_INDEX);
1426 BLI_assert(p_sorted_dummy != sorted_tris.end());
1427 int dummy_index = p_sorted_dummy - sorted_tris.begin();
1428 int prev_tri = (dummy_index == 0) ? sorted_tris[sorted_tris.size() - 1] :
1429 sorted_tris[dummy_index - 1];
1430 if (dbg_level > 0) {
1431 int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] :
1432 sorted_tris[dummy_index + 1];
1433 std::cout << "prev tri to dummy = " << prev_tri << "; next tri to dummy = " << next_tri
1434 << "\n";
1435 }
1436 const Patch &prev_patch = pinfo.patch(pinfo.tri_patch(prev_tri));
1437 if (dbg_level > 0) {
1438 std::cout << "prev_patch = " << prev_patch << "\n";
1439 }
1440 bool prev_flipped;
1441 find_flap_vert(*tm.face(prev_tri), e, &prev_flipped);
1442 int c = prev_flipped ? prev_patch.cell_below : prev_patch.cell_above;
1443 if (dbg_level > 0) {
1444 std::cout << "find_cell_for_point_near_edge returns " << c << "\n";
1445 }
1446 return c;
1447}
1448
1463static int find_ambient_cell(const IMesh &tm,
1464 const Vector<int> *component_patches,
1465 const TriMeshTopology &tmtopo,
1466 const PatchesInfo &pinfo,
1467 IMeshArena *arena)
1468{
1469 int dbg_level = 0;
1470 if (dbg_level > 0) {
1471 std::cout << "FIND_AMBIENT_CELL\n";
1472 }
1473 /* First find a vertex with the maximum x value. */
1474 /* Prefer not to populate the verts in the #IMesh just for this. */
1475 const Vert *v_extreme;
1476 auto max_x_vert = [](const Vert *a, const Vert *b) {
1477 return (a->co_exact.x > b->co_exact.x) ? a : b;
1478 };
1479 if (component_patches == nullptr) {
1480 v_extreme = threading::parallel_reduce(
1481 tm.face_index_range(),
1482 2048,
1483 (*tm.face(0))[0],
1484 [&](IndexRange range, const Vert *init) {
1485 const Vert *ans = init;
1486 for (int i : range) {
1487 const Face *f = tm.face(i);
1488 for (const Vert *v : *f) {
1489 if (v->co_exact.x > ans->co_exact.x) {
1490 ans = v;
1491 }
1492 }
1493 }
1494 return ans;
1495 },
1496 max_x_vert);
1497 }
1498 else {
1499 if (dbg_level > 0) {
1500 std::cout << "restrict to patches " << *component_patches << "\n";
1501 }
1502 int p0 = (*component_patches)[0];
1503 v_extreme = threading::parallel_reduce(
1504 component_patches->index_range(),
1505 2048,
1506 (*tm.face(pinfo.patch(p0).tri(0)))[0],
1507 [&](IndexRange range, const Vert *init) {
1508 const Vert *ans = init;
1509 for (int pi : range) {
1510 int p = (*component_patches)[pi];
1511 const Vert *tris_ans = threading::parallel_reduce(
1512 IndexRange(pinfo.patch(p).tot_tri()),
1513 2048,
1514 init,
1515 [&](IndexRange tris_range, const Vert *t_init) {
1516 const Vert *v_ans = t_init;
1517 for (int i : tris_range) {
1518 int t = pinfo.patch(p).tri(i);
1519 const Face *f = tm.face(t);
1520 for (const Vert *v : *f) {
1521 if (v->co_exact.x > v_ans->co_exact.x) {
1522 v_ans = v;
1523 }
1524 }
1525 }
1526 return v_ans;
1527 },
1528 max_x_vert);
1529 if (tris_ans->co_exact.x > ans->co_exact.x) {
1530 ans = tris_ans;
1531 }
1532 }
1533 return ans;
1534 },
1535 max_x_vert);
1536 }
1537 if (dbg_level > 0) {
1538 std::cout << "v_extreme = " << v_extreme << "\n";
1539 }
1540 /* Find edge attached to v_extreme with max absolute slope
1541 * when projected onto the XY plane. That edge is guaranteed to
1542 * be on the convex hull of the mesh. */
1543 const Span<Edge> edges = tmtopo.vert_edges(v_extreme);
1544 const mpq_class &extreme_x = v_extreme->co_exact.x;
1545 const mpq_class &extreme_y = v_extreme->co_exact.y;
1546 Edge ehull;
1547 mpq_class max_abs_slope = -1;
1548 for (Edge e : edges) {
1549 const Vert *v_other = (e.v0() == v_extreme) ? e.v1() : e.v0();
1550 const mpq3 &co_other = v_other->co_exact;
1551 mpq_class delta_x = co_other.x - extreme_x;
1552 if (delta_x == 0) {
1553 /* Vertical slope. */
1554 ehull = e;
1555 break;
1556 }
1557 mpq_class abs_slope = abs((co_other.y - extreme_y) / delta_x);
1558 if (abs_slope > max_abs_slope) {
1559 ehull = e;
1560 max_abs_slope = abs_slope;
1561 }
1562 }
1563 if (dbg_level > 0) {
1564 std::cout << "ehull = " << ehull << " slope = " << max_abs_slope << "\n";
1565 }
1566 /* Sort triangles around ehull, including a dummy triangle that include a known point in
1567 * ambient cell. */
1568 mpq3 p_in_ambient = v_extreme->co_exact;
1569 p_in_ambient.x += 1;
1570 int c_ambient = find_cell_for_point_near_edge(p_in_ambient, ehull, tm, tmtopo, pinfo, arena);
1571 if (dbg_level > 0) {
1572 std::cout << "FIND_AMBIENT_CELL returns " << c_ambient << "\n";
1573 }
1574 return c_ambient;
1575}
1576
1586static Edge find_good_sorting_edge(const Vert *testp,
1587 const Vert *closestp,
1588 const TriMeshTopology &tmtopo)
1589{
1590 constexpr int dbg_level = 0;
1591 if (dbg_level > 0) {
1592 std::cout << "FIND_GOOD_SORTING_EDGE testp = " << testp << ", closestp = " << closestp << "\n";
1593 }
1594 /* We want to project the edges incident to closestp onto a plane
1595 * whose ordinate direction will be regarded as going from closestp to testp,
1596 * and whose abscissa direction is some perpendicular to that.
1597 * A perpendicular direction can be found by swapping two coordinates
1598 * and negating one, and zeroing out the third, being careful that one
1599 * of the swapped vertices is non-zero. */
1600 const mpq3 &co_closest = closestp->co_exact;
1601 const mpq3 &co_test = testp->co_exact;
1602 BLI_assert(co_test != co_closest);
1603 mpq3 abscissa = co_test - co_closest;
1604 /* Find a non-zero-component axis of abscissa. */
1605 int axis;
1606 for (axis = 0; axis < 3; ++axis) {
1607 if (abscissa[axis] != 0) {
1608 break;
1609 }
1610 }
1611 BLI_assert(axis < 3);
1612 int axis_next = (axis + 1) % 3;
1613 int axis_next_next = (axis_next + 1) % 3;
1614 mpq3 ordinate;
1615 ordinate[axis] = abscissa[axis_next];
1616 ordinate[axis_next] = -abscissa[axis];
1617 ordinate[axis_next_next] = 0;
1618 /* By construction, dot(abscissa, ordinate) == 0, so they are perpendicular. */
1619 mpq3 normal = math::cross(abscissa, ordinate);
1620 if (dbg_level > 0) {
1621 std::cout << "abscissa = " << abscissa << "\n";
1622 std::cout << "ordinate = " << ordinate << "\n";
1623 std::cout << "normal = " << normal << "\n";
1624 }
1625 mpq_class nlen2 = math::length_squared(normal);
1626 mpq_class max_abs_slope = -1;
1627 Edge esort;
1628 const Span<Edge> edges = tmtopo.vert_edges(closestp);
1629 for (Edge e : edges) {
1630 const Vert *v_other = (e.v0() == closestp) ? e.v1() : e.v0();
1631 const mpq3 &co_other = v_other->co_exact;
1632 mpq3 evec = co_other - co_closest;
1633 /* Get projection of evec onto plane of abscissa and ordinate. */
1634 mpq3 proj_evec = evec - (math::dot(evec, normal) / nlen2) * normal;
1635 /* The projection calculations along the abscissa and ordinate should
1636 * be scaled by 1/abscissa and 1/ordinate respectively,
1637 * but we can skip: it won't affect which `evec` has the maximum slope. */
1638 mpq_class evec_a = math::dot(proj_evec, abscissa);
1639 mpq_class evec_o = math::dot(proj_evec, ordinate);
1640 if (dbg_level > 0) {
1641 std::cout << "e = " << e << "\n";
1642 std::cout << "v_other = " << v_other << "\n";
1643 std::cout << "evec = " << evec << ", proj_evec = " << proj_evec << "\n";
1644 std::cout << "evec_a = " << evec_a << ", evec_o = " << evec_o << "\n";
1645 }
1646 if (evec_a == 0) {
1647 /* evec is perpendicular to abscissa. */
1648 esort = e;
1649 if (dbg_level > 0) {
1650 std::cout << "perpendicular esort is " << esort << "\n";
1651 }
1652 break;
1653 }
1654 mpq_class abs_slope = abs(evec_o / evec_a);
1655 if (abs_slope > max_abs_slope) {
1656 esort = e;
1657 max_abs_slope = abs_slope;
1658 if (dbg_level > 0) {
1659 std::cout << "with abs_slope " << abs_slope << " new esort is " << esort << "\n";
1660 }
1661 }
1662 }
1663 return esort;
1664}
1665
1678static int find_containing_cell(const Vert *v,
1679 int t,
1680 int close_edge,
1681 int close_vert,
1682 const PatchesInfo &pinfo,
1683 const IMesh &tm,
1684 const TriMeshTopology &tmtopo,
1685 IMeshArena *arena)
1686{
1687 constexpr int dbg_level = 0;
1688 if (dbg_level > 0) {
1689 std::cout << "FIND_CONTAINING_CELL v=" << v << ", t=" << t << "\n";
1690 }
1691 const Face &tri = *tm.face(t);
1692 Edge etest;
1693 if (close_edge == -1 && close_vert == -1) {
1694 /* Choose any edge if closest point is inside the triangle. */
1695 close_edge = 0;
1696 }
1697 if (close_edge != -1) {
1698 const Vert *v0 = tri[close_edge];
1699 const Vert *v1 = tri[(close_edge + 1) % 3];
1700 const Span<Edge> edges = tmtopo.vert_edges(v0);
1701 if (dbg_level > 0) {
1702 std::cout << "look for edge containing " << v0 << " and " << v1 << "\n";
1703 std::cout << " in edges: ";
1704 for (Edge e : edges) {
1705 std::cout << e << " ";
1706 }
1707 std::cout << "\n";
1708 }
1709 for (Edge e : edges) {
1710 if ((e.v0() == v0 && e.v1() == v1) || (e.v0() == v1 && e.v1() == v0)) {
1711 etest = e;
1712 break;
1713 }
1714 }
1715 }
1716 else {
1717 int cv = close_vert;
1718 const Vert *vert_cv = tri[cv];
1719 if (vert_cv == v) {
1720 /* Need to use another one to find sorting edge. */
1721 vert_cv = tri[(cv + 1) % 3];
1722 BLI_assert(vert_cv != v);
1723 }
1724 etest = find_good_sorting_edge(v, vert_cv, tmtopo);
1725 }
1726 BLI_assert(etest.v0() != nullptr);
1727 if (dbg_level > 0) {
1728 std::cout << "etest = " << etest << "\n";
1729 }
1730 int c = find_cell_for_point_near_edge(v->co_exact, etest, tm, tmtopo, pinfo, arena);
1731 if (dbg_level > 0) {
1732 std::cout << "find_containing_cell returns " << c << "\n";
1733 }
1734 return c;
1735}
1736
1750static mpq_class closest_on_tri_to_point(const mpq3 &p,
1751 const mpq3 &a,
1752 const mpq3 &b,
1753 const mpq3 &c,
1754 mpq3 &ab,
1755 mpq3 &ac,
1756 mpq3 &ap,
1757 mpq3 &bp,
1758 mpq3 &cp,
1759 mpq3 &m,
1760 mpq3 &r,
1761 int *r_edge,
1762 int *r_vert)
1763{
1764 constexpr int dbg_level = 0;
1765 if (dbg_level > 0) {
1766 std::cout << "CLOSEST_ON_TRI_TO_POINT p = " << p << "\n";
1767 std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n";
1768 }
1769 /* Check if p in vertex region outside a. */
1770 ab = b;
1771 ab -= a;
1772 ac = c;
1773 ac -= a;
1774 ap = p;
1775 ap -= a;
1776
1777 mpq_class d1 = math::dot_with_buffer(ab, ap, m);
1778 mpq_class d2 = math::dot_with_buffer(ac, ap, m);
1779 if (d1 <= 0 && d2 <= 0) {
1780 /* Barycentric coordinates (1,0,0). */
1781 *r_edge = -1;
1782 *r_vert = 0;
1783 if (dbg_level > 0) {
1784 std::cout << " answer = a\n";
1785 }
1786 return math::distance_squared_with_buffer(p, a, m);
1787 }
1788 /* Check if p in vertex region outside b. */
1789 bp = p;
1790 bp -= b;
1791 mpq_class d3 = math::dot_with_buffer(ab, bp, m);
1792 mpq_class d4 = math::dot_with_buffer(ac, bp, m);
1793 if (d3 >= 0 && d4 <= d3) {
1794 /* Barycentric coordinates (0,1,0). */
1795 *r_edge = -1;
1796 *r_vert = 1;
1797 if (dbg_level > 0) {
1798 std::cout << " answer = b\n";
1799 }
1800 return math::distance_squared_with_buffer(p, b, m);
1801 }
1802 /* Check if p in region of ab. */
1803 mpq_class vc = d1 * d4 - d3 * d2;
1804 if (vc <= 0 && d1 >= 0 && d3 <= 0) {
1805 mpq_class v = d1 / (d1 - d3);
1806 /* Barycentric coordinates (1-v,v,0). */
1807 r = ab;
1808 r *= v;
1809 r += a;
1810 *r_vert = -1;
1811 *r_edge = 0;
1812 if (dbg_level > 0) {
1813 std::cout << " answer = on ab at " << r << "\n";
1814 }
1815 return math::distance_squared_with_buffer(p, r, m);
1816 }
1817 /* Check if p in vertex region outside c. */
1818 cp = p;
1819 cp -= c;
1820 mpq_class d5 = math::dot_with_buffer(ab, cp, m);
1821 mpq_class d6 = math::dot_with_buffer(ac, cp, m);
1822 if (d6 >= 0 && d5 <= d6) {
1823 /* Barycentric coordinates (0,0,1). */
1824 *r_edge = -1;
1825 *r_vert = 2;
1826 if (dbg_level > 0) {
1827 std::cout << " answer = c\n";
1828 }
1829 return math::distance_squared_with_buffer(p, c, m);
1830 }
1831 /* Check if p in edge region of ac. */
1832 mpq_class vb = d5 * d2 - d1 * d6;
1833 if (vb <= 0 && d2 >= 0 && d6 <= 0) {
1834 mpq_class w = d2 / (d2 - d6);
1835 /* Barycentric coordinates (1-w,0,w). */
1836 r = ac;
1837 r *= w;
1838 r += a;
1839 *r_vert = -1;
1840 *r_edge = 2;
1841 if (dbg_level > 0) {
1842 std::cout << " answer = on ac at " << r << "\n";
1843 }
1844 return math::distance_squared_with_buffer(p, r, m);
1845 }
1846 /* Check if p in edge region of bc. */
1847 mpq_class va = d3 * d6 - d5 * d4;
1848 if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
1849 mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
1850 /* Barycentric coordinates (0,1-w,w). */
1851 r = c;
1852 r -= b;
1853 r *= w;
1854 r += b;
1855 *r_vert = -1;
1856 *r_edge = 1;
1857 if (dbg_level > 0) {
1858 std::cout << " answer = on bc at " << r << "\n";
1859 }
1860 return math::distance_squared_with_buffer(p, r, m);
1861 }
1862 /* p inside face region. Compute barycentric coordinates (u,v,w). */
1863 mpq_class denom = 1 / (va + vb + vc);
1864 mpq_class v = vb * denom;
1865 mpq_class w = vc * denom;
1866 ac *= w;
1867 r = ab;
1868 r *= v;
1869 r += a;
1870 r += ac;
1871 *r_vert = -1;
1872 *r_edge = -1;
1873 if (dbg_level > 0) {
1874 std::cout << " answer = inside at " << r << "\n";
1875 }
1876 return math::distance_squared_with_buffer(p, r, m);
1877}
1878
1879static float closest_on_tri_to_point_float_dist_squared(const float3 &p,
1880 const double3 &a,
1881 const double3 &b,
1882 const double3 &c)
1883{
1884 float3 fa, fb, fc, closest;
1885 copy_v3fl_v3db(fa, a);
1887 copy_v3fl_v3db(fc, c);
1889 return len_squared_v3v3(p, closest);
1890}
1891
1892struct ComponentContainer {
1893 int containing_component{NO_INDEX};
1894 int nearest_cell{NO_INDEX};
1895 mpq_class dist_to_cell;
1896
1897 ComponentContainer(int cc, int cell, mpq_class d)
1898 : containing_component(cc), nearest_cell(cell), dist_to_cell(d)
1899 {
1900 }
1901};
1902
1909static Vector<ComponentContainer> find_component_containers(int comp,
1910 const Span<Vector<int>> components,
1911 const Span<int> ambient_cell,
1912 const IMesh &tm,
1913 const PatchesInfo &pinfo,
1914 const TriMeshTopology &tmtopo,
1915 Array<BoundingBox> &comp_bb,
1916 IMeshArena *arena)
1917{
1918 constexpr int dbg_level = 0;
1919 if (dbg_level > 0) {
1920 std::cout << "FIND_COMPONENT_CONTAINERS for comp " << comp << "\n";
1921 }
1923 int test_p = components[comp][0];
1924 int test_t = pinfo.patch(test_p).tri(0);
1925 const Vert *test_v = tm.face(test_t)[0].vert[0];
1926 if (dbg_level > 0) {
1927 std::cout << "test vertex in comp: " << test_v << "\n";
1928 }
1929 const double3 &test_v_d = test_v->co;
1930 float3 test_v_f(test_v_d[0], test_v_d[1], test_v_d[2]);
1931
1932 mpq3 buf[7];
1933
1934 for (int comp_other : components.index_range()) {
1935 if (comp == comp_other) {
1936 continue;
1937 }
1938 if (dbg_level > 0) {
1939 std::cout << "comp_other = " << comp_other << "\n";
1940 }
1941 if (!bbs_might_intersect(comp_bb[comp], comp_bb[comp_other])) {
1942 if (dbg_level > 0) {
1943 std::cout << "bounding boxes don't overlap\n";
1944 }
1945 continue;
1946 }
1947 int nearest_tri = NO_INDEX;
1948 int nearest_tri_close_vert = -1;
1949 int nearest_tri_close_edge = -1;
1950 mpq_class nearest_tri_dist_squared;
1951 float nearest_tri_dist_squared_float = FLT_MAX;
1952 for (int p : components[comp_other]) {
1953 const Patch &patch = pinfo.patch(p);
1954 for (int t : patch.tris()) {
1955 const Face &tri = *tm.face(t);
1956 if (dbg_level > 1) {
1957 std::cout << "tri " << t << " = " << &tri << "\n";
1958 }
1959 int close_vert;
1960 int close_edge;
1961 /* Try a cheap float test first. */
1962 float d2_f = closest_on_tri_to_point_float_dist_squared(
1963 test_v_f, tri[0]->co, tri[1]->co, tri[2]->co);
1964 if (d2_f - FLT_EPSILON > nearest_tri_dist_squared_float) {
1965 continue;
1966 }
1967 mpq_class d2 = closest_on_tri_to_point(test_v->co_exact,
1968 tri[0]->co_exact,
1969 tri[1]->co_exact,
1970 tri[2]->co_exact,
1971 buf[0],
1972 buf[1],
1973 buf[2],
1974 buf[3],
1975 buf[4],
1976 buf[5],
1977 buf[6],
1978 &close_edge,
1979 &close_vert);
1980 if (dbg_level > 1) {
1981 std::cout << " close_edge=" << close_edge << " close_vert=" << close_vert
1982 << " dsquared=" << d2.get_d() << "\n";
1983 }
1984 if (nearest_tri == NO_INDEX || d2 < nearest_tri_dist_squared) {
1985 nearest_tri = t;
1986 nearest_tri_close_edge = close_edge;
1987 nearest_tri_close_vert = close_vert;
1988 nearest_tri_dist_squared = d2;
1989 nearest_tri_dist_squared_float = d2_f;
1990 }
1991 }
1992 }
1993 if (dbg_level > 0) {
1994 std::cout << "closest tri to comp=" << comp << " in comp_other=" << comp_other << " is t"
1995 << nearest_tri << "\n";
1996 const Face *tn = tm.face(nearest_tri);
1997 std::cout << "tri = " << tn << "\n";
1998 std::cout << " (" << tn->vert[0]->co << "," << tn->vert[1]->co << "," << tn->vert[2]->co
1999 << ")\n";
2000 }
2001 int containing_cell = find_containing_cell(test_v,
2002 nearest_tri,
2003 nearest_tri_close_edge,
2004 nearest_tri_close_vert,
2005
2006 pinfo,
2007 tm,
2008 tmtopo,
2009 arena);
2010 if (dbg_level > 0) {
2011 std::cout << "containing cell = " << containing_cell << "\n";
2012 }
2013 if (containing_cell != ambient_cell[comp_other]) {
2014 ans.append(ComponentContainer(comp_other, containing_cell, nearest_tri_dist_squared));
2015 }
2016 }
2017 return ans;
2018}
2019
2025static void populate_comp_bbs(const Span<Vector<int>> components,
2026 const PatchesInfo &pinfo,
2027 const IMesh &im,
2028 Array<BoundingBox> &comp_bb)
2029{
2030 const int comp_grainsize = 16;
2031 /* To get a good expansion epsilon, we need to find the maximum
2032 * absolute value of any coordinate. Do it first per component,
2033 * then get the overall max. */
2034 Array<double> max_abs(components.size(), 0.0);
2035 threading::parallel_for(components.index_range(), comp_grainsize, [&](IndexRange comp_range) {
2036 for (int c : comp_range) {
2037 BoundingBox &bb = comp_bb[c];
2038 double &maxa = max_abs[c];
2039 for (int p : components[c]) {
2040 const Patch &patch = pinfo.patch(p);
2041 for (int t : patch.tris()) {
2042 const Face &tri = *im.face(t);
2043 for (const Vert *v : tri) {
2044 bb.combine(v->co);
2045 for (int i = 0; i < 3; ++i) {
2046 maxa = max_dd(maxa, fabs(v->co[i]));
2047 }
2048 }
2049 }
2050 }
2051 }
2052 });
2053 double all_max_abs = 0.0;
2054 for (int c : components.index_range()) {
2055 all_max_abs = max_dd(all_max_abs, max_abs[c]);
2056 }
2057 constexpr float pad_factor = 10.0f;
2058 float pad = all_max_abs == 0.0 ? FLT_EPSILON : 2 * FLT_EPSILON * all_max_abs;
2059 pad *= pad_factor;
2060 for (int c : components.index_range()) {
2061 comp_bb[c].expand(pad);
2062 }
2063}
2064
2072static void finish_patch_cell_graph(const IMesh &tm,
2073 CellsInfo &cinfo,
2074 PatchesInfo &pinfo,
2075 const TriMeshTopology &tmtopo,
2076 IMeshArena *arena)
2077{
2078 constexpr int dbg_level = 0;
2079 if (dbg_level > 0) {
2080 std::cout << "FINISH_PATCH_CELL_GRAPH\n";
2081 }
2082 Vector<Vector<int>> components = find_patch_components(cinfo, pinfo);
2083 if (components.size() <= 1) {
2084 if (dbg_level > 0) {
2085 std::cout << "one component so finish_patch_cell_graph does no work\n";
2086 }
2087 return;
2088 }
2089 if (dbg_level > 0) {
2090 std::cout << "components:\n";
2091 for (int comp : components.index_range()) {
2092 std::cout << comp << ": " << components[comp] << "\n";
2093 }
2094 }
2095 Array<int> ambient_cell(components.size());
2096 for (int comp : components.index_range()) {
2097 ambient_cell[comp] = find_ambient_cell(tm, &components[comp], tmtopo, pinfo, arena);
2098 }
2099 if (dbg_level > 0) {
2100 std::cout << "ambient cells:\n";
2101 for (int comp : ambient_cell.index_range()) {
2102 std::cout << comp << ": " << ambient_cell[comp] << "\n";
2103 }
2104 }
2105 int tot_components = components.size();
2106 Array<Vector<ComponentContainer>> comp_cont(tot_components);
2107 if (tot_components > 1) {
2108 Array<BoundingBox> comp_bb(tot_components);
2109 populate_comp_bbs(components, pinfo, tm, comp_bb);
2110 for (int comp : components.index_range()) {
2111 comp_cont[comp] = find_component_containers(
2112 comp, components, ambient_cell, tm, pinfo, tmtopo, comp_bb, arena);
2113 }
2114 if (dbg_level > 0) {
2115 std::cout << "component containers:\n";
2116 for (int comp : comp_cont.index_range()) {
2117 std::cout << comp << ": ";
2118 for (const ComponentContainer &cc : comp_cont[comp]) {
2119 std::cout << "[containing_comp=" << cc.containing_component
2120 << ", nearest_cell=" << cc.nearest_cell << ", d2=" << cc.dist_to_cell << "] ";
2121 }
2122 std::cout << "\n";
2123 }
2124 }
2125 }
2126 if (dbg_level > 1) {
2127 write_obj_cell_patch(tm, cinfo, pinfo, false, "beforemerge");
2128 }
2129 /* For nested components, merge their ambient cell with the nearest containing cell. */
2130 Vector<int> outer_components;
2131 for (int comp : comp_cont.index_range()) {
2132 if (comp_cont[comp].is_empty()) {
2133 outer_components.append(comp);
2134 }
2135 else {
2136 ComponentContainer &closest = comp_cont[comp][0];
2137 for (int i = 1; i < comp_cont[comp].size(); ++i) {
2138 if (comp_cont[comp][i].dist_to_cell < closest.dist_to_cell) {
2139 closest = comp_cont[comp][i];
2140 }
2141 }
2142 int comp_ambient = ambient_cell[comp];
2143 int cont_cell = closest.nearest_cell;
2144 if (dbg_level > 0) {
2145 std::cout << "merge comp " << comp << "'s ambient cell=" << comp_ambient << " to cell "
2146 << cont_cell << "\n";
2147 }
2148 merge_cells(cont_cell, comp_ambient, cinfo, pinfo);
2149 }
2150 }
2151 /* For outer components (not nested in any other component), merge their ambient cells. */
2152 if (outer_components.size() > 1) {
2153 int merged_ambient = ambient_cell[outer_components[0]];
2154 for (int i = 1; i < outer_components.size(); ++i) {
2155 if (dbg_level > 0) {
2156 std::cout << "merge comp " << outer_components[i]
2157 << "'s ambient cell=" << ambient_cell[outer_components[i]] << " to cell "
2158 << merged_ambient << "\n";
2159 }
2160 merge_cells(merged_ambient, ambient_cell[outer_components[i]], cinfo, pinfo);
2161 }
2162 }
2163 if (dbg_level > 0) {
2164 std::cout << "after FINISH_PATCH_CELL_GRAPH\nCells\n";
2165 for (int i : cinfo.index_range()) {
2166 if (cinfo.cell(i).merged_to() == NO_INDEX) {
2167 std::cout << i << ": " << cinfo.cell(i) << "\n";
2168 }
2169 }
2170 std::cout << "Patches\n";
2171 for (int i : pinfo.index_range()) {
2172 std::cout << i << ": " << pinfo.patch(i) << "\n";
2173 }
2174 if (dbg_level > 1) {
2175 write_obj_cell_patch(tm, cinfo, pinfo, false, "finished");
2176 }
2177 }
2178}
2179
2193static void propagate_windings_and_in_output_volume(PatchesInfo &pinfo,
2194 CellsInfo &cinfo,
2195 int c_ambient,
2196 BoolOpType op,
2197 int nshapes,
2198 FunctionRef<int(int)> shape_fn)
2199{
2200 int dbg_level = 0;
2201 if (dbg_level > 0) {
2202 std::cout << "PROPAGATE_WINDINGS, ambient cell = " << c_ambient << "\n";
2203 }
2204 Cell &cell_ambient = cinfo.cell(c_ambient);
2205 cell_ambient.seed_ambient_winding();
2206 /* Use a vector as a queue. It can't grow bigger than number of cells. */
2208 queue.reserve(cinfo.tot_cell());
2209 int queue_head = 0;
2210 queue.append(c_ambient);
2211 while (queue_head < queue.size()) {
2212 int c = queue[queue_head++];
2213 if (dbg_level > 1) {
2214 std::cout << "process cell " << c << "\n";
2215 }
2216 Cell &cell = cinfo.cell(c);
2217 for (int p : cell.patches()) {
2218 Patch &patch = pinfo.patch(p);
2219 bool p_above_c = patch.cell_below == c;
2220 int c_neighbor = p_above_c ? patch.cell_above : patch.cell_below;
2221 if (dbg_level > 1) {
2222 std::cout << " patch " << p << " p_above_c = " << p_above_c << "\n";
2223 std::cout << " c_neighbor = " << c_neighbor << "\n";
2224 }
2225 Cell &cell_neighbor = cinfo.cell(c_neighbor);
2226 if (!cell_neighbor.winding_assigned()) {
2227 int winding_delta = p_above_c ? -1 : 1;
2228 int t = patch.tri(0);
2229 int shape = shape_fn(t);
2230 BLI_assert(shape < nshapes);
2231 UNUSED_VARS_NDEBUG(nshapes);
2232 if (dbg_level > 1) {
2233 std::cout << " representative tri " << t << ": in shape " << shape << "\n";
2234 }
2235 cell_neighbor.set_winding_and_in_output_volume(cell, shape, winding_delta, op);
2236 if (dbg_level > 1) {
2237 std::cout << " now cell_neighbor = " << cell_neighbor << "\n";
2238 }
2239 queue.append(c_neighbor);
2240 BLI_assert(queue.size() <= cinfo.tot_cell());
2241 }
2242 }
2243 }
2244 if (dbg_level > 0) {
2245 std::cout << "\nPROPAGATE_WINDINGS result\n";
2246 for (int i = 0; i < cinfo.tot_cell(); ++i) {
2247 std::cout << i << ": " << cinfo.cell(i) << "\n";
2248 }
2249 }
2250}
2251
2261static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding)
2262{
2263 int nw = winding.size();
2264 BLI_assert(nw > 0);
2265 switch (bool_optype) {
2266 case BoolOpType::Intersect: {
2267 for (int i = 0; i < nw; ++i) {
2268 if (winding[i] == 0) {
2269 return false;
2270 }
2271 }
2272 return true;
2273 }
2274 case BoolOpType::Union: {
2275 for (int i = 0; i < nw; ++i) {
2276 if (winding[i] != 0) {
2277 return true;
2278 }
2279 }
2280 return false;
2281 }
2282 case BoolOpType::Difference: {
2283 /* if nw > 2, make it shape 0 minus the union of the rest. */
2284 if (winding[0] == 0) {
2285 return false;
2286 }
2287 if (nw == 1) {
2288 return true;
2289 }
2290 for (int i = 1; i < nw; ++i) {
2291 if (winding[i] >= 1) {
2292 return false;
2293 }
2294 }
2295 return true;
2296 }
2297 default:
2298 return false;
2299 }
2300}
2301
2307static void extract_zero_volume_cell_tris(Vector<Face *> &r_tris,
2308 const IMesh &tm_subdivided,
2309 const PatchesInfo &pinfo,
2310 const CellsInfo &cinfo,
2311 IMeshArena *arena)
2312{
2313 int dbg_level = 0;
2314 if (dbg_level > 0) {
2315 std::cout << "extract_zero_volume_cell_tris\n";
2316 }
2317 /* Find patches that are adjacent to zero-volume cells. */
2318 Array<bool> adj_to_zv(pinfo.tot_patch());
2319 for (int p : pinfo.index_range()) {
2320 const Patch &patch = pinfo.patch(p);
2321 if (cinfo.cell(patch.cell_above).zero_volume() || cinfo.cell(patch.cell_below).zero_volume()) {
2322 adj_to_zv[p] = true;
2323 }
2324 else {
2325 adj_to_zv[p] = false;
2326 }
2327 }
2328 /* Partition the adj_to_zv patches into stacks. */
2329 Vector<Vector<int>> patch_stacks;
2330 Array<bool> allocated_to_stack(pinfo.tot_patch(), false);
2331 for (int p : pinfo.index_range()) {
2332 if (!adj_to_zv[p] || allocated_to_stack[p]) {
2333 continue;
2334 }
2335 int stack_index = patch_stacks.size();
2336 patch_stacks.append(Vector<int>{p});
2337 Vector<int> &stack = patch_stacks[stack_index];
2338 Vector<bool> flipped{false};
2339 allocated_to_stack[p] = true;
2340 /* We arbitrarily choose p's above and below directions as above and below for whole stack.
2341 * Triangles in the stack that don't follow that convention are marked with flipped = true.
2342 * The non-zero-volume cell above the whole stack, following this convention, is
2343 * above_stack_cell. The non-zero-volume cell below the whole stack is #below_stack_cell. */
2344 /* First, walk in the above_cell direction from p. */
2345 int pwalk = p;
2346 const Patch *pwalk_patch = &pinfo.patch(pwalk);
2347 int c = pwalk_patch->cell_above;
2348 const Cell *cell = &cinfo.cell(c);
2349 while (cell->zero_volume()) {
2350 /* In zero-volume cells, the cell should have exactly two patches. */
2351 BLI_assert(cell->patches().size() == 2);
2352 int pother = cell->patch_other(pwalk);
2353 bool flip = pinfo.patch(pother).cell_above == c;
2354 flipped.append(flip);
2355 stack.append(pother);
2356 allocated_to_stack[pother] = true;
2357 pwalk = pother;
2358 pwalk_patch = &pinfo.patch(pwalk);
2359 c = flip ? pwalk_patch->cell_below : pwalk_patch->cell_above;
2360 cell = &cinfo.cell(c);
2361 }
2362 const Cell *above_stack_cell = cell;
2363 /* Now walk in the below_cell direction from p. */
2364 pwalk = p;
2365 pwalk_patch = &pinfo.patch(pwalk);
2366 c = pwalk_patch->cell_below;
2367 cell = &cinfo.cell(c);
2368 while (cell->zero_volume()) {
2369 BLI_assert(cell->patches().size() == 2);
2370 int pother = cell->patch_other(pwalk);
2371 bool flip = pinfo.patch(pother).cell_below == c;
2372 flipped.append(flip);
2373 stack.append(pother);
2374 allocated_to_stack[pother] = true;
2375 pwalk = pother;
2376 pwalk_patch = &pinfo.patch(pwalk);
2377 c = flip ? pwalk_patch->cell_above : pwalk_patch->cell_below;
2378 cell = &cinfo.cell(c);
2379 }
2380 const Cell *below_stack_cell = cell;
2381 if (dbg_level > 0) {
2382 std::cout << "process zero-volume patch stack " << stack << "\n";
2383 std::cout << "flipped = ";
2384 for (bool b : flipped) {
2385 std::cout << b << " ";
2386 }
2387 std::cout << "\n";
2388 }
2389 if (above_stack_cell->in_output_volume() ^ below_stack_cell->in_output_volume()) {
2390 bool need_flipped_tri = above_stack_cell->in_output_volume();
2391 if (dbg_level > 0) {
2392 std::cout << "need tri " << (need_flipped_tri ? "flipped" : "") << "\n";
2393 }
2394 int t_to_add = NO_INDEX;
2395 for (int i : stack.index_range()) {
2396 if (flipped[i] == need_flipped_tri) {
2397 t_to_add = pinfo.patch(stack[i]).tri(0);
2398 if (dbg_level > 0) {
2399 std::cout << "using tri " << t_to_add << "\n";
2400 }
2401 r_tris.append(tm_subdivided.face(t_to_add));
2402 break;
2403 }
2404 }
2405 if (t_to_add == NO_INDEX) {
2406 const Face *f = tm_subdivided.face(pinfo.patch(p).tri(0));
2407 const Face &tri = *f;
2408 /* We need flipped version or else we would have found it above. */
2409 std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]};
2410 std::array<int, 3> flipped_e_origs = {
2411 tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
2412 std::array<bool, 3> flipped_is_intersect = {
2413 tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
2414 Face *flipped_f = arena->add_face(
2415 flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect);
2416 r_tris.append(flipped_f);
2417 }
2418 }
2419 }
2420}
2421
2433static IMesh extract_from_in_output_volume_diffs(const IMesh &tm_subdivided,
2434 const PatchesInfo &pinfo,
2435 const CellsInfo &cinfo,
2436 IMeshArena *arena)
2437{
2438 constexpr int dbg_level = 0;
2439 if (dbg_level > 0) {
2440 std::cout << "\nEXTRACT_FROM_FLAG_DIFFS\n";
2441 }
2442 Vector<Face *> out_tris;
2443 out_tris.reserve(tm_subdivided.face_size());
2444 bool any_zero_volume_cell = false;
2445 for (int t : tm_subdivided.face_index_range()) {
2446 int p = pinfo.tri_patch(t);
2447 const Patch &patch = pinfo.patch(p);
2448 const Cell &cell_above = cinfo.cell(patch.cell_above);
2449 const Cell &cell_below = cinfo.cell(patch.cell_below);
2450 if (dbg_level > 0) {
2451 std::cout << "tri " << t << ": cell_above=" << patch.cell_above
2452 << " cell_below=" << patch.cell_below << "\n";
2453 std::cout << " in_output_volume_above=" << cell_above.in_output_volume()
2454 << " in_output_volume_below=" << cell_below.in_output_volume() << "\n";
2455 }
2456 bool adjacent_zero_volume_cell = cell_above.zero_volume() || cell_below.zero_volume();
2457 any_zero_volume_cell |= adjacent_zero_volume_cell;
2458 if (cell_above.in_output_volume() ^ cell_below.in_output_volume() &&
2459 !adjacent_zero_volume_cell)
2460 {
2461 bool flip = cell_above.in_output_volume();
2462 if (dbg_level > 0) {
2463 std::cout << "need tri " << t << " flip=" << flip << "\n";
2464 }
2465 Face *f = tm_subdivided.face(t);
2466 if (flip) {
2467 Face &tri = *f;
2468 std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]};
2469 std::array<int, 3> flipped_e_origs = {
2470 tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
2471 std::array<bool, 3> flipped_is_intersect = {
2472 tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
2473 Face *flipped_f = arena->add_face(
2474 flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect);
2475 out_tris.append(flipped_f);
2476 }
2477 else {
2478 out_tris.append(f);
2479 }
2480 }
2481 }
2482 if (any_zero_volume_cell) {
2483 extract_zero_volume_cell_tris(out_tris, tm_subdivided, pinfo, cinfo, arena);
2484 }
2485 return IMesh(out_tris);
2486}
2487
2488static const char *bool_optype_name(BoolOpType op)
2489{
2490 switch (op) {
2491 case BoolOpType::None:
2492 return "none";
2493 case BoolOpType::Intersect:
2494 return "intersect";
2495 case BoolOpType::Union:
2496 return "union";
2497 case BoolOpType::Difference:
2498 return "difference";
2499 default:
2500 return "<unknown>";
2501 }
2502}
2503
2504static double3 calc_point_inside_tri_db(const Face &tri)
2505{
2506 const Vert *v0 = tri.vert[0];
2507 const Vert *v1 = tri.vert[1];
2508 const Vert *v2 = tri.vert[2];
2509 double3 ans = v0->co / 3 + v1->co / 3 + v2->co / 3;
2510 return ans;
2511}
2512class InsideShapeTestData {
2513 public:
2514 const IMesh &tm;
2515 FunctionRef<int(int)> shape_fn;
2516 int nshapes;
2517 /* A per-shape vector of parity of hits of that shape. */
2518 Array<int> hit_parity;
2519
2520 InsideShapeTestData(const IMesh &tm, FunctionRef<int(int)> shape_fn, int nshapes)
2521 : tm(tm), shape_fn(shape_fn), nshapes(nshapes)
2522 {
2523 }
2524};
2525
2526static void inside_shape_callback(void *userdata,
2527 int index,
2528 const BVHTreeRay *ray,
2529 BVHTreeRayHit * /*hit*/)
2530{
2531 const int dbg_level = 0;
2532 if (dbg_level > 0) {
2533 std::cout << "inside_shape_callback, index = " << index << "\n";
2534 }
2535 InsideShapeTestData *data = static_cast<InsideShapeTestData *>(userdata);
2536 const Face &tri = *data->tm.face(index);
2537 int shape = data->shape_fn(tri.orig);
2538 if (shape == -1) {
2539 return;
2540 }
2541 float dist;
2542 float fv0[3];
2543 float fv1[3];
2544 float fv2[3];
2545 for (int i = 0; i < 3; ++i) {
2546 fv0[i] = float(tri.vert[0]->co[i]);
2547 fv1[i] = float(tri.vert[1]->co[i]);
2548 fv2[i] = float(tri.vert[2]->co[i]);
2549 }
2550 if (dbg_level > 0) {
2551 std::cout << " fv0=(" << fv0[0] << "," << fv0[1] << "," << fv0[2] << ")\n";
2552 std::cout << " fv1=(" << fv1[0] << "," << fv1[1] << "," << fv1[2] << ")\n";
2553 std::cout << " fv2=(" << fv2[0] << "," << fv2[1] << "," << fv2[2] << ")\n";
2554 }
2556 ray->origin, ray->direction, fv0, fv1, fv2, &dist, nullptr, FLT_EPSILON))
2557 {
2558 /* Count parity as +1 if ray is in the same direction as triangle's normal,
2559 * and -1 if the directions are opposite. */
2560 double3 o_db{double(ray->origin[0]), double(ray->origin[1]), double(ray->origin[2])};
2561 int parity = orient3d(tri.vert[0]->co, tri.vert[1]->co, tri.vert[2]->co, o_db);
2562 if (dbg_level > 0) {
2563 std::cout << "origin at " << o_db << ", parity = " << parity << "\n";
2564 }
2565 data->hit_parity[shape] += parity;
2566 }
2567}
2568
2578static void test_tri_inside_shapes(const IMesh &tm,
2579 FunctionRef<int(int)> shape_fn,
2580 int nshapes,
2581 int test_t_index,
2582 BVHTree *tree,
2583 Array<float> &in_shape)
2584{
2585 const int dbg_level = 0;
2586 if (dbg_level > 0) {
2587 std::cout << "test_point_inside_shapes, t_index = " << test_t_index << "\n";
2588 }
2589 Face &tri_test = *tm.face(test_t_index);
2590 int shape = shape_fn(tri_test.orig);
2591 if (shape == -1) {
2592 in_shape.fill(0.0f);
2593 return;
2594 }
2595 double3 test_point = calc_point_inside_tri_db(tri_test);
2596 /* Offset the test point a tiny bit in the tri_test normal direction. */
2597 tri_test.populate_plane(false);
2598 double3 norm = math::normalize(tri_test.plane->norm);
2599 const double offset_amount = 1e-5;
2600 double3 offset_test_point = test_point + offset_amount * norm;
2601 if (dbg_level > 0) {
2602 std::cout << "test tri is in shape " << shape << "\n";
2603 std::cout << "test point = " << test_point << "\n";
2604 std::cout << "offset_test_point = " << offset_test_point << "\n";
2605 }
2606 /* Try six test rays almost along orthogonal axes.
2607 * Perturb their directions slightly to make it less likely to hit a seam.
2608 * Ray-cast assumes they have unit length, so use r1 near 1 and
2609 * ra near 0.5, and rb near .01, but normalized so `sqrt(r1^2 + ra^2 + rb^2) == 1`. */
2610 constexpr int rays_num = 6;
2611 constexpr float r1 = 0.9987025295199663f;
2612 constexpr float ra = 0.04993512647599832f;
2613 constexpr float rb = 0.009987025295199663f;
2614 const float test_rays[rays_num][3] = {
2615 {r1, ra, rb}, {-r1, -ra, -rb}, {rb, r1, ra}, {-rb, -r1, -ra}, {ra, rb, r1}, {-ra, -rb, -r1}};
2616 InsideShapeTestData data(tm, shape_fn, nshapes);
2617 data.hit_parity = Array<int>(nshapes, 0);
2618 Array<int> count_insides(nshapes, 0);
2619 const float co[3] = {
2620 float(offset_test_point[0]), float(offset_test_point[1]), float(offset_test_point[2])};
2621 for (int i = 0; i < rays_num; ++i) {
2622 if (dbg_level > 0) {
2623 std::cout << "shoot ray " << i << "(" << test_rays[i][0] << "," << test_rays[i][1] << ","
2624 << test_rays[i][2] << ")\n";
2625 }
2626 BLI_bvhtree_ray_cast_all(tree, co, test_rays[i], 0.0f, FLT_MAX, inside_shape_callback, &data);
2627 if (dbg_level > 0) {
2628 std::cout << "ray " << i << " result:";
2629 for (int j = 0; j < nshapes; ++j) {
2630 std::cout << " " << data.hit_parity[j];
2631 }
2632 std::cout << "\n";
2633 }
2634 for (int j = 0; j < nshapes; ++j) {
2635 if (j != shape && data.hit_parity[j] > 0) {
2636 ++count_insides[j];
2637 }
2638 }
2639 data.hit_parity.fill(0);
2640 }
2641 for (int j = 0; j < nshapes; ++j) {
2642 if (j == shape) {
2643 in_shape[j] = 1.0f; /* Let's say a shape is always inside itself. */
2644 }
2645 else {
2646 in_shape[j] = float(count_insides[j]) / float(rays_num);
2647 }
2648 if (dbg_level > 0) {
2649 std::cout << "shape " << j << " inside = " << in_shape[j] << "\n";
2650 }
2651 }
2652}
2653
2660static BVHTree *raycast_tree(const IMesh &tm)
2661{
2662 BVHTree *tree = BLI_bvhtree_new(tm.face_size(), FLT_EPSILON, 4, 6);
2663 for (int i : tm.face_index_range()) {
2664 const Face *f = tm.face(i);
2665 float t_cos[9];
2666 for (int j = 0; j < 3; ++j) {
2667 const Vert *v = f->vert[j];
2668 for (int k = 0; k < 3; ++k) {
2669 t_cos[3 * j + k] = float(v->co[k]);
2670 }
2671 }
2672 BLI_bvhtree_insert(tree, i, t_cos, 3);
2673 }
2675 return tree;
2676}
2677
2682static bool raycast_test_remove(BoolOpType op, Array<int> &winding, int shape, bool *r_do_flip)
2683{
2684 constexpr int dbg_level = 0;
2685 /* Find out the "in the output volume" flag for each of the cases of winding[shape] == 0
2686 * and winding[shape] == 1. If the flags are different, this patch should be in the output.
2687 * Also, if this is a Difference and the shape isn't the first one, need to flip the normals.
2688 */
2689 winding[shape] = 0;
2690 bool in_output_volume_0 = apply_bool_op(op, winding);
2691 winding[shape] = 1;
2692 bool in_output_volume_1 = apply_bool_op(op, winding);
2693 bool do_remove = in_output_volume_0 == in_output_volume_1;
2694 bool do_flip = !do_remove && op == BoolOpType::Difference && shape != 0;
2695 if (dbg_level > 0) {
2696 std::cout << "winding = ";
2697 for (int i = 0; i < winding.size(); ++i) {
2698 std::cout << winding[i] << " ";
2699 }
2700 std::cout << "\niv0=" << in_output_volume_0 << ", iv1=" << in_output_volume_1 << "\n";
2701 std::cout << " remove=" << do_remove << ", flip=" << do_flip << "\n";
2702 }
2703 *r_do_flip = do_flip;
2704 return do_remove;
2705}
2706
2708static void raycast_add_flipped(Vector<Face *> &out_faces, const Face &tri, IMeshArena *arena)
2709{
2710
2711 Array<const Vert *> flipped_vs = {tri[0], tri[2], tri[1]};
2712 Array<int> flipped_e_origs = {tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
2713 Array<bool> flipped_is_intersect = {
2714 tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
2715 Face *flipped_f = arena->add_face(flipped_vs, tri.orig, flipped_e_origs, flipped_is_intersect);
2716 out_faces.append(flipped_f);
2717}
2718
2727static IMesh raycast_tris_boolean(
2728 const IMesh &tm, BoolOpType op, int nshapes, FunctionRef<int(int)> shape_fn, IMeshArena *arena)
2729{
2730 constexpr int dbg_level = 0;
2731 if (dbg_level > 0) {
2732 std::cout << "RAYCAST_TRIS_BOOLEAN\n";
2733 }
2734 IMesh ans;
2735 BVHTree *tree = raycast_tree(tm);
2736 Vector<Face *> out_faces;
2737 out_faces.reserve(tm.face_size());
2738# ifdef WITH_TBB
2739 tbb::spin_mutex mtx;
2740# endif
2741 const int grainsize = 256;
2742 threading::parallel_for(IndexRange(tm.face_size()), grainsize, [&](IndexRange range) {
2743 Array<float> in_shape(nshapes, 0);
2744 Array<int> winding(nshapes, 0);
2745 for (int t : range) {
2746 Face &tri = *tm.face(t);
2747 int shape = shape_fn(tri.orig);
2748 if (dbg_level > 0) {
2749 std::cout << "process triangle " << t << " = " << &tri << "\n";
2750 std::cout << "shape = " << shape << "\n";
2751 }
2752 test_tri_inside_shapes(tm, shape_fn, nshapes, t, tree, in_shape);
2753 for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
2754 if (other_shape == shape) {
2755 continue;
2756 }
2757 /* The in_shape array has a confidence value for "insideness".
2758 * For most operations, even a hint of being inside
2759 * gives good results, but when shape is a cutter in a Difference
2760 * operation, we want to be pretty sure that the point is inside other_shape.
2761 * E.g., #75827.
2762 * Also, when the operation is intersection, we also want high confidence.
2763 */
2764 bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
2765 op == BoolOpType::Intersect;
2766 bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
2767 if (dbg_level > 0) {
2768 std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
2769 << other_shape << " val = " << in_shape[other_shape] << "\n";
2770 }
2771 winding[other_shape] = inside;
2772 }
2773 bool do_flip;
2774 bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
2775 {
2776# ifdef WITH_TBB
2777 tbb::spin_mutex::scoped_lock lock(mtx);
2778# endif
2779 if (!do_remove) {
2780 if (!do_flip) {
2781 out_faces.append(&tri);
2782 }
2783 else {
2784 raycast_add_flipped(out_faces, tri, arena);
2785 }
2786 }
2787 }
2788 }
2789 });
2791 ans.set_faces(out_faces);
2792 return ans;
2793}
2794
2795/* This is (sometimes much faster) version of raycast boolean
2796 * that does it per patch rather than per triangle.
2797 * It may fail in cases where raycast_tri_boolean will succeed,
2798 * but the latter can be very slow on huge meshes. */
2799static IMesh raycast_patches_boolean(const IMesh &tm,
2800 BoolOpType op,
2801 int nshapes,
2802 FunctionRef<int(int)> shape_fn,
2803 const PatchesInfo &pinfo,
2804 IMeshArena *arena)
2805{
2806 constexpr int dbg_level = 0;
2807 if (dbg_level > 0) {
2808 std::cout << "RAYCAST_PATCHES_BOOLEAN\n";
2809 }
2810 IMesh ans;
2811 BVHTree *tree = raycast_tree(tm);
2812 Vector<Face *> out_faces;
2813 out_faces.reserve(tm.face_size());
2814 Array<float> in_shape(nshapes, 0);
2815 Array<int> winding(nshapes, 0);
2816 for (int p : pinfo.index_range()) {
2817 const Patch &patch = pinfo.patch(p);
2818 /* For test triangle, choose one in the middle of patch list
2819 * as the ones near the beginning may be very near other patches. */
2820 int test_t_index = patch.tri(patch.tot_tri() / 2);
2821 Face &tri_test = *tm.face(test_t_index);
2822 /* Assume all triangles in a patch are in the same shape. */
2823 int shape = shape_fn(tri_test.orig);
2824 if (dbg_level > 0) {
2825 std::cout << "process patch " << p << " = " << patch << "\n";
2826 std::cout << "test tri = " << test_t_index << " = " << &tri_test << "\n";
2827 std::cout << "shape = " << shape << "\n";
2828 }
2829 if (shape == -1) {
2830 continue;
2831 }
2832 test_tri_inside_shapes(tm, shape_fn, nshapes, test_t_index, tree, in_shape);
2833 for (int other_shape = 0; other_shape < nshapes; ++other_shape) {
2834 if (other_shape == shape) {
2835 continue;
2836 }
2837 bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
2838 op == BoolOpType::Intersect;
2839 bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
2840 if (dbg_level > 0) {
2841 std::cout << "test point is " << (inside ? "inside" : "outside") << " other_shape "
2842 << other_shape << " val = " << in_shape[other_shape] << "\n";
2843 }
2844 winding[other_shape] = inside;
2845 }
2846 bool do_flip;
2847 bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
2848 if (!do_remove) {
2849 for (int t : patch.tris()) {
2850 Face *f = tm.face(t);
2851 if (!do_flip) {
2852 out_faces.append(f);
2853 }
2854 else {
2855 raycast_add_flipped(out_faces, *f, arena);
2856 }
2857 }
2858 }
2859 }
2861
2862 ans.set_faces(out_faces);
2863 return ans;
2864}
2870static std::pair<int, int> find_tris_common_edge(const Face &tri1, const Face &tri2)
2871{
2872 for (int i = 0; i < 3; ++i) {
2873 for (int j = 0; j < 3; ++j) {
2874 if (tri1[(i + 1) % 3] == tri2[j] && tri1[i] == tri2[(j + 1) % 3]) {
2875 return std::pair<int, int>(i, j);
2876 }
2877 }
2878 }
2879 return std::pair<int, int>(-1, -1);
2880}
2881
2882struct MergeEdge {
2884 double len_squared = 0.0;
2885 /* v1 and v2 are the ends of the edge, ordered so that `v1->id < v2->id` */
2886 const Vert *v1 = nullptr;
2887 const Vert *v2 = nullptr;
2888 /* left_face and right_face are indices into #FaceMergeState.face. */
2889 int left_face = -1;
2890 int right_face = -1;
2891 int orig = -1; /* An edge orig index that can be used for this edge. */
2893 bool dissolvable = false;
2895 bool is_intersect = false;
2896
2897 MergeEdge() = default;
2898
2899 MergeEdge(const Vert *va, const Vert *vb)
2900 {
2901 if (va->id < vb->id) {
2902 this->v1 = va;
2903 this->v2 = vb;
2904 }
2905 else {
2906 this->v1 = vb;
2907 this->v2 = va;
2908 }
2909 };
2910};
2911
2912struct MergeFace {
2914 Vector<const Vert *> vert;
2916 Vector<int> edge;
2918 int merge_to = -1;
2920 int orig = -1;
2921};
2922struct FaceMergeState {
2926 Vector<MergeFace> face;
2931 Vector<MergeEdge> edge;
2936 Map<std::pair<int, int>, int> edge_map;
2937};
2938
2939static std::ostream &operator<<(std::ostream &os, const FaceMergeState &fms)
2940{
2941 os << "faces:\n";
2942 for (int f : fms.face.index_range()) {
2943 const MergeFace &mf = fms.face[f];
2944 std::cout << f << ": orig=" << mf.orig << " verts ";
2945 for (const Vert *v : mf.vert) {
2946 std::cout << v << " ";
2947 }
2948 std::cout << "\n";
2949 std::cout << " edges " << mf.edge << "\n";
2950 std::cout << " merge_to = " << mf.merge_to << "\n";
2951 }
2952 os << "\nedges:\n";
2953 for (int e : fms.edge.index_range()) {
2954 const MergeEdge &me = fms.edge[e];
2955 std::cout << e << ": (" << me.v1 << "," << me.v2 << ") left=" << me.left_face
2956 << " right=" << me.right_face << " dis=" << me.dissolvable << " orig=" << me.orig
2957 << " is_int=" << me.is_intersect << "\n";
2958 }
2959 return os;
2960}
2961
2972static void init_face_merge_state(FaceMergeState *fms,
2973 const Span<int> tris,
2974 const IMesh &tm,
2975 const double3 &norm)
2976{
2977 constexpr int dbg_level = 0;
2978 /* Reserve enough faces and edges so that neither will have to resize. */
2979 fms->face.reserve(tris.size() + 1);
2980 fms->edge.reserve(3 * tris.size());
2981 fms->edge_map.reserve(3 * tris.size());
2982 if (dbg_level > 0) {
2983 std::cout << "\nINIT_FACE_MERGE_STATE\n";
2984 }
2985 for (int t : tris.index_range()) {
2986 MergeFace mf;
2987 const Face &tri = *tm.face(tris[t]);
2988 if (dbg_level > 0) {
2989 std::cout << "process tri = " << &tri << "\n";
2990 }
2991 BLI_assert(tri.plane_populated());
2992 if (math::dot(norm, tri.plane->norm) <= 0.0) {
2993 if (dbg_level > 0) {
2994 std::cout << "triangle has wrong orientation, skipping\n";
2995 }
2996 continue;
2997 }
2998 mf.vert.append(tri[0]);
2999 mf.vert.append(tri[1]);
3000 mf.vert.append(tri[2]);
3001 mf.orig = tri.orig;
3002 int f = fms->face.append_and_get_index(mf);
3003 if (dbg_level > 1) {
3004 std::cout << "appended MergeFace for tri at f = " << f << "\n";
3005 }
3006 for (int i = 0; i < 3; ++i) {
3007 int inext = (i + 1) % 3;
3008 MergeEdge new_me(mf.vert[i], mf.vert[inext]);
3009 std::pair<int, int> canon_vs(new_me.v1->id, new_me.v2->id);
3010 int me_index = fms->edge_map.lookup_default(canon_vs, -1);
3011 if (dbg_level > 1) {
3012 std::cout << "new_me = canon_vs = " << new_me.v1 << ", " << new_me.v2 << "\n";
3013 std::cout << "me_index lookup = " << me_index << "\n";
3014 }
3015 if (me_index == -1) {
3016 double3 vec = new_me.v2->co - new_me.v1->co;
3017 new_me.len_squared = math::length_squared(vec);
3018 new_me.orig = tri.edge_orig[i];
3019 new_me.is_intersect = tri.is_intersect[i];
3020 new_me.dissolvable = (new_me.orig == NO_INDEX && !new_me.is_intersect);
3021 fms->edge.append(new_me);
3022 me_index = fms->edge.size() - 1;
3023 fms->edge_map.add_new(canon_vs, me_index);
3024 if (dbg_level > 1) {
3025 std::cout << "added new me with me_index = " << me_index << "\n";
3026 std::cout << " len_squared = " << new_me.len_squared << " orig = " << new_me.orig
3027 << ", is_intersect" << new_me.is_intersect
3028 << ", dissolvable = " << new_me.dissolvable << "\n";
3029 }
3030 }
3031 MergeEdge &me = fms->edge[me_index];
3032 if (dbg_level > 1) {
3033 std::cout << "retrieved me at index " << me_index << ":\n";
3034 std::cout << " v1 = " << me.v1 << " v2 = " << me.v2 << "\n";
3035 std::cout << " dis = " << me.dissolvable << " int = " << me.is_intersect << "\n";
3036 std::cout << " left_face = " << me.left_face << " right_face = " << me.right_face << "\n";
3037 }
3038 if (me.dissolvable && tri.edge_orig[i] != NO_INDEX) {
3039 if (dbg_level > 1) {
3040 std::cout << "reassigning orig to " << tri.edge_orig[i] << ", dissolvable = false\n";
3041 }
3042 me.dissolvable = false;
3043 me.orig = tri.edge_orig[i];
3044 }
3045 if (me.dissolvable && tri.is_intersect[i]) {
3046 if (dbg_level > 1) {
3047 std::cout << "reassigning dissolvable = false, is_intersect = true\n";
3048 }
3049 me.dissolvable = false;
3050 me.is_intersect = true;
3051 }
3052 /* This face is left or right depending on orientation of edge. */
3053 if (me.v1 == mf.vert[i]) {
3054 if (dbg_level > 1) {
3055 std::cout << "me.v1 == mf.vert[i] so set edge[" << me_index << "].left_face = " << f
3056 << "\n";
3057 }
3058 if (me.left_face != -1) {
3059 /* Unexpected in the normal case: this means more than one triangle shares this
3060 * edge in the same orientation. But be tolerant of this case. By making this
3061 * edge not dissolvable, we'll avoid future problems due to this non-manifold topology.
3062 */
3063 if (dbg_level > 1) {
3064 std::cout << "me.left_face was already occupied, so triangulation wasn't good\n";
3065 }
3066 me.dissolvable = false;
3067 }
3068 else {
3069 fms->edge[me_index].left_face = f;
3070 }
3071 }
3072 else {
3073 if (dbg_level > 1) {
3074 std::cout << "me.v1 != mf.vert[i] so set edge[" << me_index << "].right_face = " << f
3075 << "\n";
3076 }
3077 if (me.right_face != -1) {
3078 /* Unexpected, analogous to the me.left_face != -1 case above. */
3079 if (dbg_level > 1) {
3080 std::cout << "me.right_face was already occupied, so triangulation wasn't good\n";
3081 }
3082 me.dissolvable = false;
3083 }
3084 else {
3085 fms->edge[me_index].right_face = f;
3086 }
3087 }
3088 fms->face[f].edge.append(me_index);
3089 }
3090 }
3091 if (dbg_level > 0) {
3092 std::cout << *fms;
3093 }
3094}
3095
3102static bool dissolve_leaves_valid_bmesh(FaceMergeState *fms,
3103 const MergeEdge &me,
3104 int me_index,
3105 const MergeFace &mf_left,
3106 const MergeFace &mf_right)
3107{
3108 int a_edge_start = mf_left.edge.first_index_of_try(me_index);
3109 BLI_assert(a_edge_start != -1);
3110 int alen = mf_left.vert.size();
3111 int blen = mf_right.vert.size();
3112 int b_left_face = me.right_face;
3113 bool ok = true;
3114 /* Is there another edge, not me, in A's face, whose right face is B's left? */
3115 for (int a_e_index = (a_edge_start + 1) % alen; ok && a_e_index != a_edge_start;
3116 a_e_index = (a_e_index + 1) % alen)
3117 {
3118 const MergeEdge &a_me_cur = fms->edge[mf_left.edge[a_e_index]];
3119 if (a_me_cur.right_face == b_left_face) {
3120 ok = false;
3121 }
3122 }
3123 /* Is there a vert in A, not me.v1 or me.v2, that is also in B?
3124 * One could avoid this O(n^2) algorithm if had a structure
3125 * saying which faces a vertex touches. */
3126 for (int a_v_index = 0; ok && a_v_index < alen; ++a_v_index) {
3127 const Vert *a_v = mf_left.vert[a_v_index];
3128 if (!ELEM(a_v, me.v1, me.v2)) {
3129 for (int b_v_index = 0; b_v_index < blen; ++b_v_index) {
3130 const Vert *b_v = mf_right.vert[b_v_index];
3131 if (a_v == b_v) {
3132 ok = false;
3133 }
3134 }
3135 }
3136 }
3137 return ok;
3138}
3139
3147static void splice_faces(
3148 FaceMergeState *fms, MergeEdge &me, int me_index, MergeFace &mf_left, MergeFace &mf_right)
3149{
3150 int a_edge_start = mf_left.edge.first_index_of_try(me_index);
3151 int b_edge_start = mf_right.edge.first_index_of_try(me_index);
3152 BLI_assert(a_edge_start != -1 && b_edge_start != -1);
3153 int alen = mf_left.vert.size();
3154 int blen = mf_right.vert.size();
3155 Vector<const Vert *> splice_vert;
3156 Vector<int> splice_edge;
3157 splice_vert.reserve(alen + blen - 2);
3158 splice_edge.reserve(alen + blen - 2);
3159 int ai = 0;
3160 while (ai < a_edge_start) {
3161 splice_vert.append(mf_left.vert[ai]);
3162 splice_edge.append(mf_left.edge[ai]);
3163 ++ai;
3164 }
3165 int bi = b_edge_start + 1;
3166 while (bi != b_edge_start) {
3167 if (bi >= blen) {
3168 bi = 0;
3169 if (bi == b_edge_start) {
3170 break;
3171 }
3172 }
3173 splice_vert.append(mf_right.vert[bi]);
3174 splice_edge.append(mf_right.edge[bi]);
3175 if (mf_right.vert[bi] == fms->edge[mf_right.edge[bi]].v1) {
3176 fms->edge[mf_right.edge[bi]].left_face = me.left_face;
3177 }
3178 else {
3179 fms->edge[mf_right.edge[bi]].right_face = me.left_face;
3180 }
3181 ++bi;
3182 }
3183 ai = a_edge_start + 1;
3184 while (ai < alen) {
3185 splice_vert.append(mf_left.vert[ai]);
3186 splice_edge.append(mf_left.edge[ai]);
3187 ++ai;
3188 }
3189 mf_right.merge_to = me.left_face;
3190 mf_left.vert = splice_vert;
3191 mf_left.edge = splice_edge;
3192 me.left_face = -1;
3193 me.right_face = -1;
3194}
3195
3203static void do_dissolve(FaceMergeState *fms)
3204{
3205 const int dbg_level = 0;
3206 if (dbg_level > 1) {
3207 std::cout << "\nDO_DISSOLVE\n";
3208 }
3209 Vector<int> dissolve_edges;
3210 for (int e : fms->edge.index_range()) {
3211 if (fms->edge[e].dissolvable) {
3212 dissolve_edges.append(e);
3213 }
3214 }
3215 if (dissolve_edges.is_empty()) {
3216 return;
3217 }
3218 /* Things look nicer if we dissolve the longer edges first. */
3219 std::sort(
3220 dissolve_edges.begin(), dissolve_edges.end(), [fms](const int &a, const int &b) -> bool {
3221 return (fms->edge[a].len_squared > fms->edge[b].len_squared);
3222 });
3223 if (dbg_level > 0) {
3224 std::cout << "Sorted dissolvable edges: " << dissolve_edges << "\n";
3225 }
3226 for (int me_index : dissolve_edges) {
3227 MergeEdge &me = fms->edge[me_index];
3228 if (me.left_face == -1 || me.right_face == -1) {
3229 continue;
3230 }
3231 MergeFace &mf_left = fms->face[me.left_face];
3232 MergeFace &mf_right = fms->face[me.right_face];
3233 if (!dissolve_leaves_valid_bmesh(fms, me, me_index, mf_left, mf_right)) {
3234 continue;
3235 }
3236 if (dbg_level > 0) {
3237 std::cout << "Removing edge " << me_index << "\n";
3238 }
3239 splice_faces(fms, me, me_index, mf_left, mf_right);
3240 if (dbg_level > 1) {
3241 std::cout << "state after removal:\n";
3242 std::cout << *fms;
3243 }
3244 }
3245}
3246
3257static Vector<Face *> merge_tris_for_face(const Vector<int> &tris,
3258 const IMesh &tm,
3259 const IMesh &imesh_in,
3260 IMeshArena *arena)
3261{
3262 constexpr int dbg_level = 0;
3263 if (dbg_level > 0) {
3264 std::cout << "merge_tris_for_face\n";
3265 std::cout << "tris: " << tris << "\n";
3266 }
3267 Vector<Face *> ans;
3268 if (tris.size() <= 1) {
3269 if (tris.size() == 1) {
3270 ans.append(tm.face(tris[0]));
3271 }
3272 return ans;
3273 }
3274 bool done = false;
3275 double3 first_tri_normal = tm.face(tris[0])->plane->norm;
3276 double3 second_tri_normal = tm.face(tris[1])->plane->norm;
3277 if (tris.size() == 2 && math::dot(first_tri_normal, second_tri_normal) > 0.0) {
3278 /* Is this a case where quad with one diagonal remained unchanged?
3279 * Worth special handling because this case will be very common. */
3280 Face &tri1 = *tm.face(tris[0]);
3281 Face &tri2 = *tm.face(tris[1]);
3282 Face *in_face = imesh_in.face(tri1.orig);
3283 if (in_face->size() == 4) {
3284 std::pair<int, int> estarts = find_tris_common_edge(tri1, tri2);
3285 if (estarts.first != -1 && tri1.edge_orig[estarts.first] == NO_INDEX) {
3286 if (dbg_level > 0) {
3287 std::cout << "try recovering orig quad case\n";
3288 std::cout << "tri1 = " << &tri1 << "\n";
3289 std::cout << "tri1 = " << &tri2 << "\n";
3290 }
3291 int i0 = estarts.first;
3292 int i1 = (i0 + 1) % 3;
3293 int i2 = (i0 + 2) % 3;
3294 int j2 = (estarts.second + 2) % 3;
3295 Face tryface({tri1[i1], tri1[i2], tri1[i0], tri2[j2]}, -1, -1, {}, {});
3296 if (tryface.cyclic_equal(*in_face)) {
3297 if (dbg_level > 0) {
3298 std::cout << "inface = " << in_face << "\n";
3299 std::cout << "quad recovery worked\n";
3300 }
3301 ans.append(in_face);
3302 done = true;
3303 }
3304 }
3305 }
3306 }
3307 if (done) {
3308 return ans;
3309 }
3310
3311 double3 first_tri_normal_rev = -first_tri_normal;
3312 for (const double3 &norm : {first_tri_normal, first_tri_normal_rev}) {
3313 FaceMergeState fms;
3314 init_face_merge_state(&fms, tris, tm, norm);
3315 do_dissolve(&fms);
3316 if (dbg_level > 0) {
3317 std::cout << "faces in merged result:\n";
3318 }
3319 for (const MergeFace &mf : fms.face) {
3320 if (mf.merge_to == -1) {
3321 Array<int> e_orig(mf.edge.size());
3322 Array<bool> is_intersect(mf.edge.size());
3323 for (int i : mf.edge.index_range()) {
3324 e_orig[i] = fms.edge[mf.edge[i]].orig;
3325 is_intersect[i] = fms.edge[mf.edge[i]].is_intersect;
3326 }
3327 Face *facep = arena->add_face(mf.vert, mf.orig, e_orig, is_intersect);
3328 ans.append(facep);
3329 if (dbg_level > 0) {
3330 std::cout << " " << facep << "\n";
3331 }
3332 }
3333 }
3334 }
3335 return ans;
3336}
3337
3338static bool approx_in_line(const double3 &a, const double3 &b, const double3 &c)
3339{
3340 double3 vec1 = b - a;
3341 double3 vec2 = c - b;
3342 double cos_ang = math::dot(math::normalize(vec1), math::normalize(vec2));
3343 return fabs(cos_ang - 1.0) < 1e-4;
3344}
3345
3352static Array<bool> find_dissolve_verts(IMesh &imesh_out, int *r_count_dissolve)
3353{
3354 imesh_out.populate_vert();
3355 /* dissolve[i] will say whether imesh_out.vert(i) can be dissolved. */
3356 Array<bool> dissolve(imesh_out.vert_size());
3357 for (int v_index : imesh_out.vert_index_range()) {
3358 const Vert &vert = *imesh_out.vert(v_index);
3359 dissolve[v_index] = (vert.orig == NO_INDEX);
3360 }
3361 /* neighbors[i] will be a pair giving the up-to-two neighboring vertices
3362 * of the vertex v in position i of imesh_out.vert.
3363 * If we encounter a third, then v will not be dissolvable. */
3365 imesh_out.vert_size(), std::pair<const Vert *, const Vert *>(nullptr, nullptr));
3366 for (int f : imesh_out.face_index_range()) {
3367 const Face &face = *imesh_out.face(f);
3368 for (int i : face.index_range()) {
3369 const Vert *v = face[i];
3370 int v_index = imesh_out.lookup_vert(v);
3371 BLI_assert(v_index != NO_INDEX);
3372 if (dissolve[v_index]) {
3373 const Vert *n1 = face[face.next_pos(i)];
3374 const Vert *n2 = face[face.prev_pos(i)];
3375 const Vert *f_n1 = neighbors[v_index].first;
3376 const Vert *f_n2 = neighbors[v_index].second;
3377 if (f_n1 != nullptr) {
3378 /* Already has a neighbor in another face; can't dissolve unless they are the same. */
3379 if (!((n1 == f_n2 && n2 == f_n1) || (n1 == f_n1 && n2 == f_n2))) {
3380 /* Different neighbors, so can't dissolve. */
3381 dissolve[v_index] = false;
3382 }
3383 }
3384 else {
3385 /* These are the first-seen neighbors. */
3386 neighbors[v_index] = std::pair<const Vert *, const Vert *>(n1, n2);
3387 }
3388 }
3389 }
3390 }
3391 int count = 0;
3392 for (int v_out : imesh_out.vert_index_range()) {
3393 if (dissolve[v_out]) {
3394 dissolve[v_out] = false; /* Will set back to true if final condition is satisfied. */
3395 const std::pair<const Vert *, const Vert *> &nbrs = neighbors[v_out];
3396 if (nbrs.first != nullptr) {
3397 BLI_assert(nbrs.second != nullptr);
3398 const Vert *v_v_out = imesh_out.vert(v_out);
3399 if (approx_in_line(nbrs.first->co, v_v_out->co, nbrs.second->co)) {
3400 dissolve[v_out] = true;
3401 ++count;
3402 }
3403 }
3404 }
3405 }
3406 if (r_count_dissolve != nullptr) {
3407 *r_count_dissolve = count;
3408 }
3409 return dissolve;
3410}
3411
3417static void dissolve_verts(IMesh *imesh, const Array<bool> dissolve, IMeshArena *arena)
3418{
3419 constexpr int inline_face_size = 100;
3420 Vector<bool, inline_face_size> face_pos_erase;
3421 bool any_faces_erased = false;
3422 for (int f : imesh->face_index_range()) {
3423 const Face &face = *imesh->face(f);
3424 face_pos_erase.clear();
3425 int erase_num = 0;
3426 for (const Vert *v : face) {
3427 int v_index = imesh->lookup_vert(v);
3428 BLI_assert(v_index != NO_INDEX);
3429 if (dissolve[v_index]) {
3430 face_pos_erase.append(true);
3431 ++erase_num;
3432 }
3433 else {
3434 face_pos_erase.append(false);
3435 }
3436 }
3437 if (erase_num > 0) {
3438 any_faces_erased |= imesh->erase_face_positions(f, face_pos_erase, arena);
3439 }
3440 }
3441 imesh->set_dirty_verts();
3442 if (any_faces_erased) {
3443 imesh->remove_null_faces();
3444 }
3445}
3446
3458static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out,
3459 const IMesh &imesh_in,
3460 IMeshArena *arena)
3461{
3462 const int dbg_level = 0;
3463 if (dbg_level > 1) {
3464 std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n";
3465 }
3466 /* For now: need plane normals for all triangles. */
3467 const int grainsize = 1024;
3468 threading::parallel_for(tm_out.face_index_range(), grainsize, [&](IndexRange range) {
3469 for (int i : range) {
3470 Face *tri = tm_out.face(i);
3471 tri->populate_plane(false);
3472 }
3473 });
3474 /* Gather all output triangles that are part of each input face.
3475 * face_output_tris[f] will be indices of triangles in tm_out
3476 * that have f as their original face. */
3477 int tot_in_face = imesh_in.face_size();
3478 Array<Vector<int>> face_output_tris(tot_in_face);
3479 for (int t : tm_out.face_index_range()) {
3480 const Face &tri = *tm_out.face(t);
3481 int in_face = tri.orig;
3482 face_output_tris[in_face].append(t);
3483 }
3484 if (dbg_level > 1) {
3485 std::cout << "face_output_tris:\n";
3486 for (int f : face_output_tris.index_range()) {
3487 std::cout << f << ": " << face_output_tris[f] << "\n";
3488 }
3489 }
3490
3491 /* Merge triangles that we can from face_output_tri to make faces for output.
3492 * face_output_face[f] will be new original const Face *'s that
3493 * make up whatever part of the boolean output remains of input face f. */
3494 Array<Vector<Face *>> face_output_face(tot_in_face);
3495 int tot_out_face = 0;
3496 for (int in_f : imesh_in.face_index_range()) {
3497 if (dbg_level > 1) {
3498 std::cout << "merge tris for face " << in_f << "\n";
3499 }
3500 int out_tris_for_face_num = face_output_tris.size();
3501 if (out_tris_for_face_num == 0) {
3502 continue;
3503 }
3504 face_output_face[in_f] = merge_tris_for_face(face_output_tris[in_f], tm_out, imesh_in, arena);
3505 tot_out_face += face_output_face[in_f].size();
3506 }
3507 Array<Face *> face(tot_out_face);
3508 int out_f_index = 0;
3509 for (int in_f : imesh_in.face_index_range()) {
3510 const Span<Face *> f_faces = face_output_face[in_f];
3511 if (f_faces.size() > 0) {
3512 std::copy(f_faces.begin(), f_faces.end(), &face[out_f_index]);
3513 out_f_index += f_faces.size();
3514 }
3515 }
3516 IMesh imesh_out(face);
3517 /* Dissolve vertices that were (a) not original; and (b) now have valence 2 and
3518 * are between two other vertices that are exactly in line with them.
3519 * These were created because of triangulation edges that have been dissolved. */
3520 int count_dissolve;
3521 Array<bool> v_dissolve = find_dissolve_verts(imesh_out, &count_dissolve);
3522 if (count_dissolve > 0) {
3523 dissolve_verts(&imesh_out, v_dissolve, arena);
3524 }
3525 if (dbg_level > 1) {
3526 write_obj_mesh(imesh_out, "boolean_post_dissolve");
3527 }
3528
3529 return imesh_out;
3530}
3531
3532IMesh boolean_trimesh(IMesh &tm_in,
3533 BoolOpType op,
3534 int nshapes,
3535 FunctionRef<int(int)> shape_fn,
3536 bool use_self,
3537 bool hole_tolerant,
3538 IMeshArena *arena)
3539{
3540 constexpr int dbg_level = 0;
3541 if (dbg_level > 0) {
3542 std::cout << "BOOLEAN of " << nshapes << " operand" << (nshapes == 1 ? "" : "s")
3543 << " op=" << bool_optype_name(op) << "\n";
3544 if (dbg_level > 1) {
3545 tm_in.populate_vert();
3546 std::cout << "boolean_trimesh input:\n" << tm_in;
3547 write_obj_mesh(tm_in, "boolean_in");
3548 }
3549 }
3550 if (tm_in.face_size() == 0) {
3551 return IMesh(tm_in);
3552 }
3553# ifdef PERFDEBUG
3554 double start_time = BLI_time_now_seconds();
3555 std::cout << " boolean_trimesh, timing begins\n";
3556# endif
3557
3558 IMesh tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena);
3559 if (dbg_level > 1) {
3560 write_obj_mesh(tm_si, "boolean_tm_si");
3561 std::cout << "\nboolean_tm_input after intersection:\n" << tm_si;
3562 }
3563# ifdef PERFDEBUG
3564 double intersect_time = BLI_time_now_seconds();
3565 std::cout << " intersected, time = " << intersect_time - start_time << "\n";
3566# endif
3567
3568 /* It is possible for tm_si to be empty if all the input triangles are bogus/degenerate. */
3569 if (tm_si.face_size() == 0 || op == BoolOpType::None) {
3570 return tm_si;
3571 }
3572 auto si_shape_fn = [shape_fn, tm_si](int t) { return shape_fn(tm_si.face(t)->orig); };
3573 TriMeshTopology tm_si_topo(tm_si);
3574# ifdef PERFDEBUG
3575 double topo_time = BLI_time_now_seconds();
3576 std::cout << " topology built, time = " << topo_time - intersect_time << "\n";
3577# endif
3578 bool pwn = is_pwn(tm_si, tm_si_topo);
3579# ifdef PERFDEBUG
3580 double pwn_time = BLI_time_now_seconds();
3581 std::cout << " pwn checked, time = " << pwn_time - topo_time << "\n";
3582# endif
3583 IMesh tm_out;
3584 if (!pwn) {
3585 if (dbg_level > 0) {
3586 std::cout << "Input is not PWN, using raycast method\n";
3587 }
3588 if (hole_tolerant) {
3589 tm_out = raycast_tris_boolean(tm_si, op, nshapes, shape_fn, arena);
3590 }
3591 else {
3592 PatchesInfo pinfo = find_patches(tm_si, tm_si_topo);
3593 tm_out = raycast_patches_boolean(tm_si, op, nshapes, shape_fn, pinfo, arena);
3594 }
3595# ifdef PERFDEBUG
3596 double raycast_time = BLI_time_now_seconds();
3597 std::cout << " raycast_boolean done, time = " << raycast_time - pwn_time << "\n";
3598# endif
3599 }
3600 else {
3601 PatchesInfo pinfo = find_patches(tm_si, tm_si_topo);
3602# ifdef PERFDEBUG
3603 double patch_time = BLI_time_now_seconds();
3604 std::cout << " patches found, time = " << patch_time - pwn_time << "\n";
3605# endif
3606 CellsInfo cinfo = find_cells(tm_si, tm_si_topo, pinfo);
3607 if (dbg_level > 0) {
3608 std::cout << "Input is PWN\n";
3609 }
3610# ifdef PERFDEBUG
3611 double cell_time = BLI_time_now_seconds();
3612 std::cout << " cells found, time = " << cell_time - pwn_time << "\n";
3613# endif
3614 finish_patch_cell_graph(tm_si, cinfo, pinfo, tm_si_topo, arena);
3615# ifdef PERFDEBUG
3616 double finish_pc_time = BLI_time_now_seconds();
3617 std::cout << " finished patch-cell graph, time = " << finish_pc_time - cell_time << "\n";
3618# endif
3619 bool pc_ok = patch_cell_graph_ok(cinfo, pinfo);
3620 if (!pc_ok) {
3621 /* TODO: if bad input can lead to this, diagnose the problem. */
3622 std::cout << "Something funny about input or a bug in boolean\n";
3623 return IMesh(tm_in);
3624 }
3625 cinfo.init_windings(nshapes);
3626 int c_ambient = find_ambient_cell(tm_si, nullptr, tm_si_topo, pinfo, arena);
3627# ifdef PERFDEBUG
3628 double amb_time = BLI_time_now_seconds();
3629 std::cout << " ambient cell found, time = " << amb_time - finish_pc_time << "\n";
3630# endif
3631 if (c_ambient == NO_INDEX) {
3632 /* TODO: find a way to propagate this error to user properly. */
3633 std::cout << "Could not find an ambient cell; input not valid?\n";
3634 return IMesh(tm_si);
3635 }
3636 propagate_windings_and_in_output_volume(pinfo, cinfo, c_ambient, op, nshapes, si_shape_fn);
3637# ifdef PERFDEBUG
3638 double propagate_time = BLI_time_now_seconds();
3639 std::cout << " windings propagated, time = " << propagate_time - amb_time << "\n";
3640# endif
3641 tm_out = extract_from_in_output_volume_diffs(tm_si, pinfo, cinfo, arena);
3642# ifdef PERFDEBUG
3643 double extract_time = BLI_time_now_seconds();
3644 std::cout << " extracted, time = " << extract_time - propagate_time << "\n";
3645# endif
3646 if (dbg_level > 0) {
3647 /* Check if output is PWN. */
3648 TriMeshTopology tm_out_topo(tm_out);
3649 if (!is_pwn(tm_out, tm_out_topo)) {
3650 std::cout << "OUTPUT IS NOT PWN!\n";
3651 }
3652 }
3653 }
3654 if (dbg_level > 1) {
3655 write_obj_mesh(tm_out, "boolean_tm_output");
3656 std::cout << "boolean tm output:\n" << tm_out;
3657 }
3658# ifdef PERFDEBUG
3659 double end_time = BLI_time_now_seconds();
3660 std::cout << " boolean_trimesh done, total time = " << end_time - start_time << "\n";
3661# endif
3662 return tm_out;
3663}
3664
3665static void dump_test_spec(IMesh &imesh)
3666{
3667 std::cout << "test spec = " << imesh.vert_size() << " " << imesh.face_size() << "\n";
3668 for (const Vert *v : imesh.vertices()) {
3669 std::cout << v->co_exact[0] << " " << v->co_exact[1] << " " << v->co_exact[2] << " # "
3670 << v->co[0] << " " << v->co[1] << " " << v->co[2] << "\n";
3671 }
3672 for (const Face *f : imesh.faces()) {
3673 for (const Vert *fv : *f) {
3674 std::cout << imesh.lookup_vert(fv) << " ";
3675 }
3676 std::cout << "\n";
3677 }
3678}
3679
3680IMesh boolean_mesh(IMesh &imesh,
3681 BoolOpType op,
3682 int nshapes,
3683 FunctionRef<int(int)> shape_fn,
3684 bool use_self,
3685 bool hole_tolerant,
3686 IMesh *imesh_triangulated,
3687 IMeshArena *arena)
3688{
3689 constexpr int dbg_level = 0;
3690 if (dbg_level > 0) {
3691 std::cout << "\nBOOLEAN_MESH\n"
3692 << nshapes << " operand" << (nshapes == 1 ? "" : "s")
3693 << " op=" << bool_optype_name(op) << "\n";
3694 if (dbg_level > 1) {
3695 write_obj_mesh(imesh, "boolean_mesh_in");
3696 std::cout << imesh;
3697 if (dbg_level > 2) {
3698 dump_test_spec(imesh);
3699 }
3700 }
3701 }
3702 IMesh *tm_in = imesh_triangulated;
3703 IMesh our_triangulation;
3704# ifdef PERFDEBUG
3705 double start_time = BLI_time_now_seconds();
3706 std::cout << "boolean_mesh, timing begins\n";
3707# endif
3708 if (tm_in == nullptr) {
3709 our_triangulation = triangulate_polymesh(imesh, arena);
3710 tm_in = &our_triangulation;
3711 }
3712# ifdef PERFDEBUG
3713 double tri_time = BLI_time_now_seconds();
3714 std::cout << "triangulated, time = " << tri_time - start_time << "\n";
3715# endif
3716 if (dbg_level > 1) {
3717 write_obj_mesh(*tm_in, "boolean_tm_in");
3718 }
3719 IMesh tm_out = boolean_trimesh(*tm_in, op, nshapes, shape_fn, use_self, hole_tolerant, arena);
3720# ifdef PERFDEBUG
3721 double bool_tri_time = BLI_time_now_seconds();
3722 std::cout << "boolean_trimesh done, time = " << bool_tri_time - tri_time << "\n";
3723# endif
3724 if (dbg_level > 1) {
3725 std::cout << "bool_trimesh_output:\n" << tm_out;
3726 write_obj_mesh(tm_out, "bool_trimesh_output");
3727 }
3728 IMesh ans = polymesh_from_trimesh_with_dissolve(tm_out, imesh, arena);
3729# ifdef PERFDEBUG
3730 double dissolve_time = BLI_time_now_seconds();
3731 std::cout << "polymesh from dissolving, time = " << dissolve_time - bool_tri_time << "\n";
3732# endif
3733 if (dbg_level > 0) {
3734 std::cout << "boolean_mesh output:\n" << ans;
3735 if (dbg_level > 2) {
3736 ans.populate_vert();
3737 dump_test_spec(ans);
3738 }
3739 }
3740# ifdef PERFDEBUG
3741 double end_time = BLI_time_now_seconds();
3742 std::cout << "boolean_mesh done, total time = " << end_time - start_time << "\n";
3743# endif
3744 return ans;
3745}
3746
3747} // namespace blender::meshintersect
3748
3749#endif // WITH_GMP
#define BLI_assert(a)
Definition BLI_assert.h:50
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
void BLI_bvhtree_ray_cast_all(const BVHTree *tree, const float co[3], const float dir[3], float radius, float hit_dist, BVHTree_RayCastCallback callback, void *userdata)
MINLINE double max_dd(double a, double b)
Math vector functions needed specifically for mesh intersect and boolean.
void closest_on_tri_to_point_v3(float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3])
bool isect_ray_tri_epsilon_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2], float epsilon)
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3fl_v3db(float r[3], const double a[3])
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
double BLI_time_now_seconds(void)
Definition time.c:65
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
typedef double(DMatrix)[4][4]
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Map
bool operator==(const AssetWeakReference &a, const AssetWeakReference &b)
int pad[32 - sizeof(int)]
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btVector3 & operator[](int i)
Get a mutable reference to a row of the matrix as a vector.
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
bool closest(btVector3 &v)
int64_t size() const
Definition BLI_array.hh:245
const T * end() const
Definition BLI_array.hh:314
void fill(const T &value) const
Definition BLI_array.hh:261
const T * begin() const
Definition BLI_array.hh:310
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
void print_stats(const char *name) const
Definition BLI_map.hh:918
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:531
ValueIterator values() const
Definition BLI_map.hh:846
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
ItemIterator items() const
Definition BLI_map.hh:864
void reserve(int64_t n)
Definition BLI_map.hh:979
Patch()
Definition subd/patch.h:15
int64_t size() const
Definition BLI_set.hh:564
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
void add_new(const Key &key)
Definition BLI_set.hh:233
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr const T * begin() const
Definition BLI_span.hh:221
constexpr bool is_empty() const
Definition BLI_span.hh:261
bool is_empty() const
Definition BLI_stack.hh:308
T pop()
Definition BLI_stack.hh:242
void push(const T &value)
Definition BLI_stack.hh:213
int64_t size() const
int64_t append_and_get_index(const T &value)
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
void reserve(const int64_t min_capacity)
void append_non_duplicates(const T &value)
T * end()
const T & first() const
T * begin()
void clear()
local_group_size(16, 16) .push_constant(Type b
KDTree_3d * tree
draw_view in_light_buf[] float
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
BLI_INLINE float fb(float length, float L)
blender::Vector< blender::nodes::ItemDeclarationPtr >::const_iterator ItemIterator
int count
ccl_device_inline float len_squared(const float2 a)
ccl_device_inline float2 fabs(const float2 a)
ThreadQueue * queue
all scheduled work for the cpu
T length_squared(const VecBase< T, Size > &a)
std::ostream & operator<<(std::ostream &stream, EulerOrder order)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
void index(const bNode &, void *r_value)
void normal(const bNode &, void *r_value)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d)
VecBase< double, 3 > double3
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
static void init(bNodeTree *, bNode *node)
#define hash
Definition noise.c:154
#define FLT_MAX
Definition stdcycles.h:14
unsigned __int64 uint64_t
Definition stdint.h:90
float origin[3]
Definition BLI_kdopbvh.h:55
float direction[3]
Definition BLI_kdopbvh.h:57
ccl_device_inline int abs(int x)
Definition util/math.h:120
std::ostream & operator<<(std::ostream &stream, bUUID uuid)
Definition uuid.cc:131