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
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_;
127 Map<const Vert *, Vector<Edge>> vert_edges_;
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;
1349 Vector<std::pair<Edge, Vector<int> *>> tris;
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);
1888 closest_on_tri_to_point_v3(closest, p, fa, fb, fc);
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 }
1922 Vector<ComponentContainer> ans;
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. */
2207 Vector<int> queue;
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. */
3364 Array<std::pair<const Vert *, const Vert *>> neighbors(
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]
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
void init()
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)
Patch()
Definition subd/patch.h:15
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)
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 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
#define hash
Definition noise.c:154
#define FLT_MAX
Definition stdcycles.h:14
unsigned __int64 uint64_t
Definition stdint.h:90
float co[3]
ccl_device_inline int abs(int x)
Definition util/math.h:120
std::ostream & operator<<(std::ostream &stream, bUUID uuid)
Definition uuid.cc:131