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