61 const Vert *v_[2]{
nullptr,
nullptr};
67 if (v0->id <=
v1->id) {
77 const Vert *v0()
const
94 return v_[0]->id == other.v_[0]->id && v_[1]->id == other.v_[1]->id;
105 if (
e.v0() ==
nullptr) {
110 os <<
"(" <<
e.v0() <<
"," <<
e.v1() <<
")";
115 static std::ostream &
operator<<(std::ostream &os,
const Span<int> &
a)
117 for (
int i :
a.index_range()) {
119 if (i !=
a.size() - 1) {
126 static std::ostream &
operator<<(std::ostream &os,
const Array<int> &iarr)
128 os << Span<int>(iarr);
133 class TriMeshTopology : NonCopyable {
135 Map<Edge, Vector<int> *> edge_tri_;
137 Map<const Vert *, Vector<Edge>> vert_edges_;
140 TriMeshTopology(
const IMesh &tm);
145 int other_tri_if_manifold(
Edge e,
int t)
const
147 if (edge_tri_.contains(
e)) {
148 auto *p = edge_tri_.lookup(
e);
149 if (p->size() == 2) {
150 return ((*p)[0] ==
t) ? (*p)[1] : (*p)[0];
157 const Vector<int> *edge_tris(
Edge e)
const
159 return edge_tri_.lookup_default(
e,
nullptr);
164 const Vector<Edge> &vert_edges(
const Vert *
v)
const
166 return vert_edges_.lookup(
v);
169 Map<Edge, Vector<int> *>::ItemIterator edge_tri_map_items()
const
171 return edge_tri_.items();
175 TriMeshTopology::TriMeshTopology(
const IMesh &tm)
177 const int dbg_level = 0;
179 std::cout <<
"TRIMESHTOPOLOGY CONSTRUCTION\n";
183 const int estimate_num_edges = 2 * tm.face_size();
184 const int estimate_num_verts = tm.face_size();
185 edge_tri_.reserve(estimate_num_edges);
186 vert_edges_.reserve(estimate_num_verts);
187 for (
int t : tm.face_index_range()) {
188 const Face &tri = *tm.face(
t);
190 for (
int i = 0; i < 3; ++i) {
191 const Vert *
v = tri[i];
192 const Vert *vnext = tri[(i + 1) % 3];
194 Vector<Edge> *edges = vert_edges_.lookup_ptr(
v);
195 if (edges ==
nullptr) {
196 vert_edges_.add_new(
v, Vector<Edge>());
197 edges = vert_edges_.lookup_ptr(
v);
200 edges->append_non_duplicates(
e);
201 auto createf = [
t](Vector<int> **pvec) { *pvec =
new Vector<int>{
t}; };
202 auto modifyf = [
t](Vector<int> **pvec) { (*pvec)->append_non_duplicates(
t); };
203 this->edge_tri_.add_or_modify(
Edge(
v, vnext), createf, modifyf);
208 std::cout <<
"After TriMeshTopology construction\n";
209 for (
auto item : edge_tri_.items()) {
210 std::cout <<
"tris for edge " << item.key <<
": " << *item.value <<
"\n";
211 constexpr
bool print_stats =
false;
213 edge_tri_.print_stats();
216 for (
auto item : vert_edges_.items()) {
217 std::cout <<
"edges for vert " << item.key <<
":\n";
218 for (
const Edge &
e : item.value) {
219 std::cout <<
" " <<
e <<
"\n";
226 TriMeshTopology::~TriMeshTopology()
228 for (
const Vector<int> *vec : edge_tri_.values()) {
255 IndexRange tri_range()
const
257 return IndexRange(tri_.size());
260 Span<int> tris()
const
262 return Span<int>(tri_);
265 int cell_above{NO_INDEX};
266 int cell_below{NO_INDEX};
272 os <<
"Patch " << patch.tris();
273 if (patch.cell_above != NO_INDEX) {
274 os <<
" cell_above=" << patch.cell_above;
277 os <<
" cell_above not set";
279 if (patch.cell_below != NO_INDEX) {
280 os <<
" cell_below=" << patch.cell_below;
283 os <<
" cell_below not set";
290 Vector<Patch> patch_;
292 Array<int> tri_patch_;
294 Map<std::pair<int, int>,
Edge> pp_edge_;
297 explicit PatchesInfo(
int ntri)
299 constexpr
int max_expected_patch_patch_incidences = 100;
300 tri_patch_ = Array<int>(ntri, NO_INDEX);
301 pp_edge_.reserve(max_expected_patch_patch_incidences);
304 int tri_patch(
int t)
const
306 return tri_patch_[
t];
311 int patch_index = patch_.append_and_get_index(
Patch());
315 void grow_patch(
int patch_index,
int t)
317 tri_patch_[
t] = patch_index;
318 patch_[patch_index].add_tri(
t);
321 bool tri_is_assigned(
int t)
const
323 return tri_patch_[
t] != NO_INDEX;
326 const Patch &patch(
int patch_index)
const
328 return patch_[patch_index];
331 Patch &patch(
int patch_index)
333 return patch_[patch_index];
336 int tot_patch()
const
338 return patch_.size();
341 IndexRange index_range()
const
343 return IndexRange(patch_.size());
346 const Patch *begin()
const
348 return patch_.begin();
351 const Patch *end()
const
358 return patch_.begin();
366 void add_new_patch_patch_edge(
int p1,
int p2,
Edge e)
368 pp_edge_.add_new(std::pair<int, int>(p1, p2),
e);
369 pp_edge_.add_new(std::pair<int, int>(p2, p1),
e);
372 Edge patch_patch_edge(
int p1,
int p2)
374 return pp_edge_.lookup_default(std::pair<int, int>(p1, p2),
Edge());
377 const Map<std::pair<int, int>,
Edge> &patch_patch_edge_map()
383 static bool apply_bool_op(BoolOpType bool_optype,
const Array<int> &winding);
393 int merged_to_{NO_INDEX};
394 bool winding_assigned_{
false};
396 bool in_output_volume_{
false};
399 bool zero_volume_{
false};
404 void add_patch(
int p)
407 zero_volume_ =
false;
410 const Set<int> &patches()
const
416 int patch_other(
int p)
const
418 if (patches_.size() != 2) {
421 for (
int pother : patches_) {
429 Span<int> winding()
const
431 return Span<int>(winding_);
434 void init_winding(
int winding_len)
436 winding_ = Array<int>(winding_len);
439 void seed_ambient_winding()
442 winding_assigned_ =
true;
445 void set_winding_and_in_output_volume(
const Cell &from_cell,
448 BoolOpType bool_optype)
450 std::copy(from_cell.winding().begin(), from_cell.winding().end(), winding_.begin());
452 winding_[shape] += delta;
454 winding_assigned_ =
true;
455 in_output_volume_ = apply_bool_op(bool_optype, winding_);
458 bool in_output_volume()
const
460 return in_output_volume_;
463 bool winding_assigned()
const
465 return winding_assigned_;
468 bool zero_volume()
const
473 int merged_to()
const
478 void set_merged_to(
int c)
487 void check_for_zero_volume(
const PatchesInfo &pinfo,
const IMesh &
mesh);
490 static std::ostream &
operator<<(std::ostream &os,
const Cell &cell)
492 os <<
"Cell patches";
493 for (
int p : cell.patches()) {
494 std::cout <<
" " << p;
496 if (cell.winding().size() > 0) {
497 os <<
" winding=" << cell.winding();
498 os <<
" in_output_volume=" << cell.in_output_volume();
500 os <<
" zv=" << cell.zero_volume();
505 static bool tris_have_same_verts(
const IMesh &
mesh,
int t1,
int t2)
509 BLI_assert(tri1.size() == 3 && tri2.size() == 3);
510 if (tri1.vert[0] == tri2.vert[0]) {
511 return ((tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[2]) ||
512 (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[1]));
514 if (tri1.vert[0] == tri2.vert[1]) {
515 return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[2]) ||
516 (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[0]));
518 if (tri1.vert[0] == tri2.vert[2]) {
519 return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[1]) ||
520 (tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[0]));
530 void Cell::check_for_zero_volume(
const PatchesInfo &pinfo,
const IMesh &
mesh)
532 if (patches_.size() == 2) {
533 int p1_index = NO_INDEX;
534 int p2_index = NO_INDEX;
535 for (
int p : patches_) {
536 if (p1_index == NO_INDEX) {
543 BLI_assert(p1_index != NO_INDEX && p2_index != NO_INDEX);
544 const Patch &p1 = pinfo.patch(p1_index);
545 const Patch &p2 = pinfo.patch(p2_index);
546 if (p1.tot_tri() == 1 && p2.tot_tri() == 1) {
547 if (tris_have_same_verts(
mesh, p1.tri(0), p2.tri(0))) {
559 CellsInfo() =
default;
563 int index = cell_.append_and_get_index(Cell());
572 const Cell &cell(
int c)
const
582 IndexRange index_range()
const
584 return cell_.index_range();
587 const Cell *begin()
const
589 return cell_.begin();
592 const Cell *end()
const
599 return cell_.begin();
607 void init_windings(
int winding_len)
609 for (Cell &cell : cell_) {
610 cell.init_winding(winding_len);
618 static void write_obj_cell_patch(
const IMesh &m,
619 const CellsInfo &cinfo,
620 const PatchesInfo &pinfo,
622 const std::string &name)
630 const char *objdir =
"/tmp/";
633 std::string fname = std::string(objdir) + name + std::string(
"_cellpatch.obj");
637 std::cout <<
"Could not open file " << fname <<
"\n";
644 f <<
"o cellpatch\n";
645 for (
const Vert *
v : mm.vertices()) {
646 const double3 dv =
v->
co;
647 f <<
"v " << dv[0] <<
" " << dv[1] <<
" " << dv[2] <<
"\n";
650 for (
int p : pinfo.index_range()) {
651 f <<
"g patch" << p <<
"\n";
652 const Patch &patch = pinfo.patch(p);
653 for (
int t : patch.tris()) {
654 const Face &tri = *mm.face(
t);
656 for (
const Vert *
v : tri) {
657 f << mm.lookup_vert(
v) + 1 <<
" ";
663 for (
int c : cinfo.index_range()) {
664 f <<
"g cell" <<
c <<
"\n";
665 const Cell &cell = cinfo.cell(
c);
666 for (
int p : cell.patches()) {
667 const Patch &patch = pinfo.patch(p);
668 for (
int t : patch.tris()) {
669 const Face &tri = *mm.face(
t);
671 for (
const Vert *
v : tri) {
672 f << mm.lookup_vert(
v) + 1 <<
" ";
681 static void merge_cells(
int merge_to,
int merge_from, CellsInfo &cinfo, PatchesInfo &pinfo)
683 if (merge_to == merge_from) {
686 Cell &merge_from_cell = cinfo.cell(merge_from);
687 Cell &merge_to_cell = cinfo.cell(merge_to);
688 int final_merge_to = merge_to;
689 while (merge_to_cell.merged_to() != NO_INDEX) {
690 final_merge_to = merge_to_cell.merged_to();
691 merge_to_cell = cinfo.cell(final_merge_to);
693 for (
int cell_p : merge_from_cell.patches()) {
694 merge_to_cell.add_patch(cell_p);
695 Patch &patch = pinfo.patch(cell_p);
696 if (patch.cell_above == merge_from) {
697 patch.cell_above = merge_to;
699 if (patch.cell_below == merge_from) {
700 patch.cell_below = merge_to;
703 merge_from_cell.set_merged_to(final_merge_to);
709 static PatchesInfo find_patches(
const IMesh &tm,
const TriMeshTopology &tmtopo)
711 const int dbg_level = 0;
713 std::cout <<
"\nFIND_PATCHES\n";
715 int ntri = tm.face_size();
716 PatchesInfo pinfo(ntri);
718 Stack<int> cur_patch_grow;
719 for (
int t : tm.face_index_range()) {
720 if (pinfo.tri_patch(
t) == -1) {
721 cur_patch_grow.push(
t);
722 int cur_patch_index = pinfo.add_patch();
723 while (!cur_patch_grow.is_empty()) {
724 int tcand = cur_patch_grow.pop();
726 std::cout <<
"pop tcand = " << tcand <<
"; assigned = " << pinfo.tri_is_assigned(tcand)
729 if (pinfo.tri_is_assigned(tcand)) {
733 std::cout <<
"grow patch from seed tcand=" << tcand <<
"\n";
735 pinfo.grow_patch(cur_patch_index, tcand);
736 const Face &tri = *tm.face(tcand);
737 for (
int i = 0; i < 3; ++i) {
738 Edge e(tri[i], tri[(i + 1) % 3]);
739 int t_other = tmtopo.other_tri_if_manifold(
e, tcand);
741 std::cout <<
" edge " <<
e <<
" generates t_other=" << t_other <<
"\n";
743 if (t_other != NO_INDEX) {
744 if (!pinfo.tri_is_assigned(t_other)) {
746 std::cout <<
" push t_other = " << t_other <<
"\n";
748 cur_patch_grow.push(t_other);
754 std::cout <<
" e non-manifold case\n";
756 const Vector<int> *etris = tmtopo.edge_tris(
e);
757 if (etris !=
nullptr) {
758 for (
int i : etris->index_range()) {
759 int t_other = (*etris)[i];
760 if (t_other != tcand && pinfo.tri_is_assigned(t_other)) {
761 int p_other = pinfo.tri_patch(t_other);
762 if (p_other == cur_patch_index) {
765 if (pinfo.patch_patch_edge(cur_patch_index, p_other).v0() ==
nullptr) {
766 pinfo.add_new_patch_patch_edge(cur_patch_index, p_other,
e);
768 std::cout <<
"added patch_patch_edge (" << cur_patch_index <<
"," << p_other
769 <<
") = " <<
e <<
"\n";
781 std::cout <<
"\nafter FIND_PATCHES: found " << pinfo.tot_patch() <<
" patches\n";
782 for (
int p : pinfo.index_range()) {
783 std::cout << p <<
": " << pinfo.patch(p) <<
"\n";
786 std::cout <<
"\ntriangle map\n";
787 for (
int t : tm.face_index_range()) {
788 std::cout <<
t <<
": " << tm.face(
t) <<
" patch " << pinfo.tri_patch(
t) <<
"\n";
791 std::cout <<
"\npatch-patch incidences\n";
792 for (
int p1 : pinfo.index_range()) {
793 for (
int p2 : pinfo.index_range()) {
794 Edge e = pinfo.patch_patch_edge(p1, p2);
795 if (
e.v0() !=
nullptr) {
796 std::cout <<
"p" << p1 <<
" and p" << p2 <<
" share edge " <<
e <<
"\n";
810 static const Vert *find_flap_vert(
const Face &tri,
const Edge e,
bool *r_rev)
814 if (tri[0] ==
e.v0()) {
815 if (tri[1] ==
e.v1()) {
820 if (tri[2] !=
e.v1()) {
827 else if (tri[1] ==
e.v0()) {
828 if (tri[2] ==
e.v1()) {
833 if (tri[0] !=
e.v1()) {
841 if (tri[2] !=
e.v0()) {
844 if (tri[0] ==
e.v1()) {
849 if (tri[1] !=
e.v1()) {
873 static int sort_tris_class(
const Face &tri,
const Face &tri0,
const Edge e)
875 const int dbg_level = 0;
877 std::cout <<
"classify e = " <<
e <<
"\n";
879 mpq3 a0 = tri0[0]->co_exact;
880 mpq3 a1 = tri0[1]->co_exact;
881 mpq3 a2 = tri0[2]->co_exact;
884 const Vert *flapv0 = find_flap_vert(tri0,
e, &rev0);
885 const Vert *flapv = find_flap_vert(tri,
e, &rev);
887 std::cout <<
" t0 = " << tri0[0] <<
" " << tri0[1] <<
" " << tri0[2];
888 std::cout <<
" rev0 = " << rev0 <<
" flapv0 = " << flapv0 <<
"\n";
889 std::cout <<
" t = " << tri[0] <<
" " << tri[1] <<
" " << tri[2];
890 std::cout <<
" rev = " << rev <<
" flapv = " << flapv <<
"\n";
892 BLI_assert(flapv !=
nullptr && flapv0 !=
nullptr);
893 const mpq3 flap = flapv->co_exact;
895 int orient =
orient3d(a0, a1, a2, flap);
900 else if (orient < 0) {
904 ans = flapv == flapv0 ? 1 : 2;
907 std::cout <<
" orient = " << orient <<
" ans = " << ans <<
"\n";
912 constexpr
int EXTRA_TRI_INDEX = INT_MAX;
920 static void sort_by_signed_triangle_index(Vector<int> &g,
923 const Face *extra_tri)
925 Array<int> signed_g(g.size());
926 for (
int i : g.index_range()) {
927 const Face &tri = g[i] == EXTRA_TRI_INDEX ? *extra_tri : *tm.face(g[i]);
929 find_flap_vert(tri,
e, &rev);
930 signed_g[i] = rev ? -g[i] : g[i];
932 std::sort(signed_g.begin(), signed_g.end());
934 for (
int i : g.index_range()) {
935 g[i] =
abs(signed_g[i]);
953 static Array<int> sort_tris_around_edge(
const IMesh &tm,
954 const TriMeshTopology &tmtopo,
956 const Span<int> tris,
958 const Face *extra_tri)
970 const int dbg_level = 0;
971 if (tris.size() == 0) {
978 std::cout <<
"sort_tris_around_edge " <<
e <<
"\n";
979 std::cout <<
"tris = " << tris <<
"\n";
980 std::cout <<
"t0 = " << t0 <<
"\n";
982 Vector<int> g1{tris[0]};
986 std::array<Vector<int> *, 4> groups = {&g1, &g2, &g3, &g4};
987 const Face &triref = *tm.face(tris[0]);
988 for (
int i : tris.index_range()) {
993 BLI_assert(
t < tm.face_size() || (
t == EXTRA_TRI_INDEX && extra_tri !=
nullptr));
994 const Face &tri = (
t == EXTRA_TRI_INDEX) ? *extra_tri : *tm.face(
t);
996 std::cout <<
"classifying tri " <<
t <<
" with respect to " << tris[0] <<
"\n";
998 int group_num = sort_tris_class(tri, triref,
e);
1000 std::cout <<
" classify result : " << group_num <<
"\n";
1002 groups[group_num - 1]->append(
t);
1004 if (dbg_level > 1) {
1005 std::cout <<
"g1 = " << g1 <<
"\n";
1006 std::cout <<
"g2 = " << g2 <<
"\n";
1007 std::cout <<
"g3 = " << g3 <<
"\n";
1008 std::cout <<
"g4 = " << g4 <<
"\n";
1010 if (g1.size() > 1) {
1011 sort_by_signed_triangle_index(g1,
e, tm, extra_tri);
1012 if (dbg_level > 1) {
1013 std::cout <<
"g1 sorted: " << g1 <<
"\n";
1016 if (g2.size() > 1) {
1017 sort_by_signed_triangle_index(g2,
e, tm, extra_tri);
1018 if (dbg_level > 1) {
1019 std::cout <<
"g2 sorted: " << g2 <<
"\n";
1022 if (g3.size() > 1) {
1023 Array<int> g3sorted = sort_tris_around_edge(tm, tmtopo,
e, g3, t0, extra_tri);
1024 std::copy(g3sorted.begin(), g3sorted.end(), g3.begin());
1025 if (dbg_level > 1) {
1026 std::cout <<
"g3 sorted: " << g3 <<
"\n";
1029 if (g4.size() > 1) {
1030 Array<int> g4sorted = sort_tris_around_edge(tm, tmtopo,
e, g4, t0, extra_tri);
1031 std::copy(g4sorted.begin(), g4sorted.end(), g4.begin());
1032 if (dbg_level > 1) {
1033 std::cout <<
"g4 sorted: " << g4 <<
"\n";
1036 int group_tot_size = g1.size() + g2.size() + g3.size() + g4.size();
1037 Array<int> ans(group_tot_size);
1038 int *p = ans.begin();
1039 if (tris[0] == t0) {
1051 if (dbg_level > 0) {
1052 std::cout <<
"sorted tris = " << ans <<
"\n";
1063 static void find_cells_from_edge(
const IMesh &tm,
1064 const TriMeshTopology &tmtopo,
1069 const int dbg_level = 0;
1070 if (dbg_level > 0) {
1071 std::cout <<
"FIND_CELLS_FROM_EDGE " <<
e <<
"\n";
1073 const Vector<int> *edge_tris = tmtopo.edge_tris(
e);
1075 Array<int> sorted_tris = sort_tris_around_edge(
1076 tm, tmtopo,
e, Span<int>(*edge_tris), (*edge_tris)[0],
nullptr);
1078 int n_edge_tris = edge_tris->size();
1079 Array<int> edge_patches(n_edge_tris);
1080 for (
int i = 0; i < n_edge_tris; ++i) {
1081 edge_patches[i] = pinfo.tri_patch(sorted_tris[i]);
1082 if (dbg_level > 1) {
1083 std::cout <<
"edge_patches[" << i <<
"] = " << edge_patches[i] <<
"\n";
1086 for (
int i = 0; i < n_edge_tris; ++i) {
1087 int inext = (i + 1) % n_edge_tris;
1088 int r_index = edge_patches[i];
1089 int rnext_index = edge_patches[inext];
1090 Patch &
r = pinfo.patch(r_index);
1091 Patch &rnext = pinfo.patch(rnext_index);
1094 find_flap_vert(*tm.face(sorted_tris[i]),
e, &r_flipped);
1095 find_flap_vert(*tm.face(sorted_tris[inext]),
e, &rnext_flipped);
1096 int *r_follow_cell = r_flipped ? &
r.cell_below : &
r.cell_above;
1097 int *rnext_prev_cell = rnext_flipped ? &rnext.cell_above : &rnext.cell_below;
1098 if (dbg_level > 0) {
1099 std::cout <<
"process patch pair " << r_index <<
" " << rnext_index <<
"\n";
1100 std::cout <<
" r_flipped = " << r_flipped <<
" rnext_flipped = " << rnext_flipped <<
"\n";
1101 std::cout <<
" r_follow_cell (" << (r_flipped ?
"below" :
"above")
1102 <<
") = " << *r_follow_cell <<
"\n";
1103 std::cout <<
" rnext_prev_cell (" << (rnext_flipped ?
"above" :
"below")
1104 <<
") = " << *rnext_prev_cell <<
"\n";
1106 if (*r_follow_cell == NO_INDEX && *rnext_prev_cell == NO_INDEX) {
1108 int c = cinfo.add_cell();
1110 *rnext_prev_cell =
c;
1111 Cell &cell = cinfo.cell(
c);
1112 cell.add_patch(r_index);
1113 cell.add_patch(rnext_index);
1114 cell.check_for_zero_volume(pinfo, tm);
1115 if (dbg_level > 0) {
1116 std::cout <<
" made new cell " <<
c <<
"\n";
1117 std::cout <<
" p" << r_index <<
"." << (r_flipped ?
"cell_below" :
"cell_above") <<
" = c"
1119 std::cout <<
" p" << rnext_index <<
"." << (rnext_flipped ?
"cell_above" :
"cell_below")
1120 <<
" = c" <<
c <<
"\n";
1123 else if (*r_follow_cell != NO_INDEX && *rnext_prev_cell == NO_INDEX) {
1124 int c = *r_follow_cell;
1125 *rnext_prev_cell =
c;
1126 Cell &cell = cinfo.cell(
c);
1127 cell.add_patch(rnext_index);
1128 cell.check_for_zero_volume(pinfo, tm);
1129 if (dbg_level > 0) {
1130 std::cout <<
" reuse r_follow: p" << rnext_index <<
"."
1131 << (rnext_flipped ?
"cell_above" :
"cell_below") <<
" = c" <<
c <<
"\n";
1134 else if (*r_follow_cell == NO_INDEX && *rnext_prev_cell != NO_INDEX) {
1135 int c = *rnext_prev_cell;
1137 Cell &cell = cinfo.cell(
c);
1138 cell.add_patch(r_index);
1139 cell.check_for_zero_volume(pinfo, tm);
1140 if (dbg_level > 0) {
1141 std::cout <<
" reuse rnext prev: rprev_p" << r_index <<
"."
1142 << (r_flipped ?
"cell_below" :
"cell_above") <<
" = c" <<
c <<
"\n";
1146 if (*r_follow_cell != *rnext_prev_cell) {
1147 int follow_cell_num_patches = cinfo.cell(*r_follow_cell).patches().size();
1148 int prev_cell_num_patches = cinfo.cell(*rnext_prev_cell).patches().size();
1149 if (follow_cell_num_patches >= prev_cell_num_patches) {
1150 if (dbg_level > 0) {
1151 std::cout <<
" merge cell " << *rnext_prev_cell <<
" into cell " << *r_follow_cell
1154 merge_cells(*r_follow_cell, *rnext_prev_cell, cinfo, pinfo);
1158 if (dbg_level > 0) {
1159 std::cout <<
" merge cell " << *r_follow_cell <<
" into cell " << *rnext_prev_cell
1162 merge_cells(*rnext_prev_cell, *r_follow_cell, cinfo, pinfo);
1172 static CellsInfo find_cells(
const IMesh &tm,
const TriMeshTopology &tmtopo, PatchesInfo &pinfo)
1174 const int dbg_level = 0;
1175 if (dbg_level > 0) {
1176 std::cout <<
"\nFIND_CELLS\n";
1180 Set<Edge> processed_edges;
1181 for (
const auto item : pinfo.patch_patch_edge_map().items()) {
1182 int p = item.key.first;
1183 int q = item.key.second;
1185 const Edge &
e = item.value;
1186 if (!processed_edges.contains(
e)) {
1187 processed_edges.add_new(
e);
1188 find_cells_from_edge(tm, tmtopo, pinfo, cinfo,
e);
1197 for (
int p : pinfo.index_range()) {
1198 Patch &patch = pinfo.patch(p);
1199 if (patch.cell_above == NO_INDEX) {
1200 int c = cinfo.add_cell();
1201 patch.cell_above =
c;
1202 Cell &cell = cinfo.cell(
c);
1205 if (patch.cell_below == NO_INDEX) {
1206 int c = cinfo.add_cell();
1207 patch.cell_below =
c;
1208 Cell &cell = cinfo.cell(
c);
1212 if (dbg_level > 0) {
1213 std::cout <<
"\nFIND_CELLS found " << cinfo.tot_cell() <<
" cells\nCells\n";
1214 for (
int i : cinfo.index_range()) {
1215 std::cout << i <<
": " << cinfo.cell(i) <<
"\n";
1217 std::cout <<
"Patches\n";
1218 for (
int i : pinfo.index_range()) {
1219 std::cout << i <<
": " << pinfo.patch(i) <<
"\n";
1221 if (dbg_level > 1) {
1222 write_obj_cell_patch(tm, cinfo, pinfo,
false,
"postfindcells");
1233 static Vector<Vector<int>> find_patch_components(
const CellsInfo &cinfo, PatchesInfo &pinfo)
1235 constexpr
int dbg_level = 0;
1236 if (dbg_level > 0) {
1237 std::cout <<
"FIND_PATCH_COMPONENTS\n";
1239 if (pinfo.tot_patch() == 0) {
1240 return Vector<Vector<int>>();
1242 int current_component = 0;
1243 Array<bool> cell_processed(cinfo.tot_cell(),
false);
1245 Vector<Vector<int>> ans;
1246 for (
int pstart : pinfo.index_range()) {
1247 Patch &patch_pstart = pinfo.patch(pstart);
1248 if (patch_pstart.component != NO_INDEX) {
1251 ans.append(Vector<int>());
1252 ans[current_component].append(pstart);
1254 patch_pstart.component = current_component;
1255 while (!stack.is_empty()) {
1256 int p = stack.pop();
1257 Patch &patch = pinfo.patch(p);
1258 BLI_assert(patch.component == current_component);
1259 for (
int c : {patch.cell_above, patch.cell_below}) {
1260 if (cell_processed[
c]) {
1263 cell_processed[
c] =
true;
1264 for (
int pn : cinfo.cell(
c).patches()) {
1265 Patch &patch_neighbor = pinfo.patch(pn);
1266 if (patch_neighbor.component == NO_INDEX) {
1267 patch_neighbor.component = current_component;
1269 ans[current_component].append(pn);
1274 ++current_component;
1276 if (dbg_level > 0) {
1277 std::cout <<
"found " << ans.size() <<
" components\n";
1278 for (
int comp : ans.index_range()) {
1279 std::cout << comp <<
": " << ans[comp] <<
"\n";
1289 static bool patch_cell_graph_ok(
const CellsInfo &cinfo,
const PatchesInfo &pinfo)
1291 for (
int c : cinfo.index_range()) {
1292 const Cell &cell = cinfo.cell(
c);
1293 if (cell.merged_to() != NO_INDEX) {
1296 if (cell.patches().size() == 0) {
1297 std::cout <<
"Patch/Cell graph disconnected at Cell " <<
c <<
" with no patches\n";
1300 for (
int p : cell.patches()) {
1301 if (p >= pinfo.tot_patch()) {
1302 std::cout <<
"Patch/Cell graph has bad patch index at Cell " <<
c <<
"\n";
1307 for (
int p : pinfo.index_range()) {
1308 const Patch &patch = pinfo.patch(p);
1309 if (patch.cell_above == NO_INDEX || patch.cell_below == NO_INDEX) {
1310 std::cout <<
"Patch/Cell graph disconnected at Patch " << p
1311 <<
" with one or two missing cells\n";
1314 if (patch.cell_above >= cinfo.tot_cell() || patch.cell_below >= cinfo.tot_cell()) {
1315 std::cout <<
"Patch/Cell graph has bad cell index at Patch " << p <<
"\n";
1335 static bool is_pwn(
const IMesh &tm,
const TriMeshTopology &tmtopo)
1337 constexpr
int dbg_level = 0;
1338 for (
auto item : tmtopo.edge_tri_map_items()) {
1339 const Edge &edge = item.key;
1343 for (
int t : *item.value) {
1344 const Face &face = *tm.face(
t);
1346 for (
int i : face.index_range()) {
1347 if (face[i] == edge.v0()) {
1348 if (face[(i + 1) % 3] == edge.
v1()) {
1358 if (tot_orient != 0) {
1359 if (dbg_level > 0) {
1360 std::cout <<
"edge causing non-pwn: " << edge <<
"\n";
1375 static int find_cell_for_point_near_edge(mpq3 p,
1378 const TriMeshTopology &tmtopo,
1379 const PatchesInfo &pinfo,
1382 constexpr
int dbg_level = 0;
1383 if (dbg_level > 0) {
1384 std::cout <<
"FIND_CELL_FOR_POINT_NEAR_EDGE, p=" << p <<
" e=" <<
e <<
"\n";
1386 const Vector<int> *etris = tmtopo.edge_tris(
e);
1387 const Vert *dummy_vert = arena->add_or_find_vert(p, NO_INDEX);
1388 const Face *dummy_tri = arena->add_face({
e.v0(),
e.v1(), dummy_vert},
1390 {NO_INDEX, NO_INDEX, NO_INDEX},
1391 {
false,
false,
false});
1393 Array<int> edge_tris(etris->size() + 1);
1394 std::copy(etris->begin(), etris->end(), edge_tris.begin());
1395 edge_tris[edge_tris.size() - 1] = EXTRA_TRI_INDEX;
1396 Array<int> sorted_tris = sort_tris_around_edge(
1397 tm, tmtopo,
e, edge_tris, edge_tris[0], dummy_tri);
1398 if (dbg_level > 0) {
1399 std::cout <<
"sorted tris = " << sorted_tris <<
"\n";
1401 int *p_sorted_dummy = std::find(sorted_tris.begin(), sorted_tris.end(), EXTRA_TRI_INDEX);
1402 BLI_assert(p_sorted_dummy != sorted_tris.end());
1403 int dummy_index = p_sorted_dummy - sorted_tris.begin();
1404 int prev_tri = (dummy_index == 0) ? sorted_tris[sorted_tris.size() - 1] :
1405 sorted_tris[dummy_index - 1];
1406 int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] :
1407 sorted_tris[dummy_index + 1];
1408 if (dbg_level > 0) {
1409 std::cout <<
"prev tri to dummy = " << prev_tri <<
"; next tri to dummy = " << next_tri
1412 const Patch &prev_patch = pinfo.patch(pinfo.tri_patch(prev_tri));
1413 if (dbg_level > 0) {
1414 std::cout <<
"prev_patch = " << prev_patch <<
"\n";
1417 find_flap_vert(*tm.face(prev_tri),
e, &prev_flipped);
1418 int c = prev_flipped ? prev_patch.cell_below : prev_patch.cell_above;
1419 if (dbg_level > 0) {
1420 std::cout <<
"find_cell_for_point_near_edge returns " <<
c <<
"\n";
1439 static int find_ambient_cell(
const IMesh &tm,
1440 const Vector<int> *component_patches,
1441 const TriMeshTopology &tmtopo,
1442 const PatchesInfo &pinfo,
1446 if (dbg_level > 0) {
1447 std::cout <<
"FIND_AMBIENT_CELL\n";
1451 const Vert *v_extreme;
1452 mpq_class extreme_x;
1453 if (component_patches ==
nullptr) {
1454 v_extreme = (*tm.face(0))[0];
1455 extreme_x = v_extreme->co_exact.x;
1456 for (
const Face *f : tm.faces()) {
1457 for (
const Vert *
v : *f) {
1458 const mpq_class &
x =
v->co_exact.x;
1459 if (
x > extreme_x) {
1467 if (dbg_level > 0) {
1468 std::cout <<
"restrict to patches " << *component_patches <<
"\n";
1470 int p0 = (*component_patches)[0];
1471 v_extreme = (*tm.face(pinfo.patch(p0).tri(0)))[0];
1472 extreme_x = v_extreme->co_exact.x;
1473 for (
int p : *component_patches) {
1474 for (
int t : pinfo.patch(p).tris()) {
1475 const Face *f = tm.face(
t);
1476 for (
const Vert *
v : *f) {
1477 const mpq_class &
x =
v->co_exact.x;
1478 if (
x > extreme_x) {
1486 if (dbg_level > 0) {
1487 std::cout <<
"v_extreme = " << v_extreme <<
"\n";
1492 const Vector<Edge> &edges = tmtopo.vert_edges(v_extreme);
1493 const mpq_class extreme_y = v_extreme->co_exact.y;
1495 mpq_class max_abs_slope = -1;
1496 for (
Edge e : edges) {
1497 const Vert *v_other = (
e.v0() == v_extreme) ?
e.v1() :
e.v0();
1498 const mpq3 &co_other = v_other->co_exact;
1499 mpq_class delta_x = co_other.x - extreme_x;
1505 mpq_class abs_slope =
abs((co_other.y - extreme_y) / delta_x);
1506 if (abs_slope > max_abs_slope) {
1508 max_abs_slope = abs_slope;
1511 if (dbg_level > 0) {
1512 std::cout <<
"ehull = " << ehull <<
" slope = " << max_abs_slope <<
"\n";
1516 mpq3 p_in_ambient = v_extreme->co_exact;
1517 p_in_ambient.x += 1;
1518 int c_ambient = find_cell_for_point_near_edge(p_in_ambient, ehull, tm, tmtopo, pinfo, arena);
1519 if (dbg_level > 0) {
1520 std::cout <<
"FIND_AMBIENT_CELL returns " << c_ambient <<
"\n";
1534 static Edge find_good_sorting_edge(
const Vert *testp,
1535 const Vert *closestp,
1536 const TriMeshTopology &tmtopo)
1538 constexpr
int dbg_level = 0;
1539 if (dbg_level > 0) {
1540 std::cout <<
"FIND_GOOD_SORTING_EDGE testp = " << testp <<
", closestp = " << closestp <<
"\n";
1548 const mpq3 &co_closest = closestp->co_exact;
1549 const mpq3 &co_test = testp->co_exact;
1551 mpq3 abscissa = co_test - co_closest;
1554 for (axis = 0; axis < 3; ++axis) {
1555 if (abscissa[axis] != 0) {
1560 int axis_next = (axis + 1) % 3;
1561 int axis_next_next = (axis_next + 1) % 3;
1563 ordinate[axis] = abscissa[axis_next];
1564 ordinate[axis_next] = -abscissa[axis];
1565 ordinate[axis_next_next] = 0;
1568 if (dbg_level > 0) {
1569 std::cout <<
"abscissa = " << abscissa <<
"\n";
1570 std::cout <<
"ordinate = " << ordinate <<
"\n";
1571 std::cout <<
"normal = " <<
normal <<
"\n";
1573 mpq_class nlen2 =
normal.length_squared();
1574 mpq_class max_abs_slope = -1;
1576 const Vector<Edge> &edges = tmtopo.vert_edges(closestp);
1577 for (
Edge e : edges) {
1578 const Vert *v_other = (
e.v0() == closestp) ?
e.v1() :
e.v0();
1579 const mpq3 &co_other = v_other->co_exact;
1580 mpq3 evec = co_other - co_closest;
1586 mpq_class evec_a =
mpq3::dot(proj_evec, abscissa);
1587 mpq_class evec_o =
mpq3::dot(proj_evec, ordinate);
1588 if (dbg_level > 0) {
1589 std::cout <<
"e = " <<
e <<
"\n";
1590 std::cout <<
"v_other = " << v_other <<
"\n";
1591 std::cout <<
"evec = " << evec <<
", proj_evec = " << proj_evec <<
"\n";
1592 std::cout <<
"evec_a = " << evec_a <<
", evec_o = " << evec_o <<
"\n";
1597 if (dbg_level > 0) {
1598 std::cout <<
"perpendicular esort is " << esort <<
"\n";
1602 mpq_class abs_slope =
abs(evec_o / evec_a);
1603 if (abs_slope > max_abs_slope) {
1605 max_abs_slope = abs_slope;
1606 if (dbg_level > 0) {
1607 std::cout <<
"with abs_slope " << abs_slope <<
" new esort is " << esort <<
"\n";
1626 static int find_containing_cell(
const Vert *
v,
1630 const PatchesInfo &pinfo,
1632 const TriMeshTopology &tmtopo,
1635 constexpr
int dbg_level = 0;
1636 if (dbg_level > 0) {
1637 std::cout <<
"FIND_CONTAINING_CELL v=" <<
v <<
", t=" <<
t <<
"\n";
1639 const Face &tri = *tm.face(
t);
1641 if (close_edge == -1 && close_vert == -1) {
1645 if (close_edge != -1) {
1646 const Vert *v0 = tri[close_edge];
1647 const Vert *
v1 = tri[(close_edge + 1) % 3];
1648 const Vector<Edge> &edges = tmtopo.vert_edges(v0);
1649 if (dbg_level > 0) {
1650 std::cout <<
"look for edge containing " << v0 <<
" and " <<
v1 <<
"\n";
1651 std::cout <<
" in edges: ";
1652 for (
Edge e : edges) {
1653 std::cout <<
e <<
" ";
1657 for (
Edge e : edges) {
1658 if ((
e.v0() == v0 &&
e.v1() ==
v1) || (
e.v0() ==
v1 &&
e.v1() == v0)) {
1665 int cv = close_vert;
1666 const Vert *vert_cv = tri[cv];
1669 vert_cv = tri[(cv + 1) % 3];
1672 etest = find_good_sorting_edge(
v, vert_cv, tmtopo);
1675 if (dbg_level > 0) {
1676 std::cout <<
"etest = " << etest <<
"\n";
1678 int c = find_cell_for_point_near_edge(
v->co_exact, etest, tm, tmtopo, pinfo, arena);
1679 if (dbg_level > 0) {
1680 std::cout <<
"find_containing_cell returns " <<
c <<
"\n";
1694 static mpq_class closest_on_tri_to_point(
1695 const mpq3 &p,
const mpq3 &
a,
const mpq3 &b,
const mpq3 &
c,
int *r_edge,
int *r_vert)
1697 constexpr
int dbg_level = 0;
1698 if (dbg_level > 0) {
1699 std::cout <<
"CLOSEST_ON_TRI_TO_POINT p = " << p <<
"\n";
1700 std::cout <<
" a = " <<
a <<
", b = " << b <<
", c = " <<
c <<
"\n";
1708 if (d1 <= 0 && d2 <= 0) {
1712 if (dbg_level > 0) {
1713 std::cout <<
" answer = a\n";
1715 return mpq3::distance_squared(p,
a);
1721 if (d3 >= 0 && d4 <= d3) {
1725 if (dbg_level > 0) {
1726 std::cout <<
" answer = b\n";
1728 return mpq3::distance_squared(p, b);
1731 mpq_class vc = d1 * d4 - d3 * d2;
1732 if (vc <= 0 && d1 >= 0 && d3 <= 0) {
1733 mpq_class
v = d1 / (d1 - d3);
1735 mpq3
r =
a +
v * ab;
1738 if (dbg_level > 0) {
1739 std::cout <<
" answer = on ab at " <<
r <<
"\n";
1741 return mpq3::distance_squared(p,
r);
1747 if (d6 >= 0 && d5 <= d6) {
1751 if (dbg_level > 0) {
1752 std::cout <<
" answer = c\n";
1754 return mpq3::distance_squared(p,
c);
1757 mpq_class vb = d5 * d2 - d1 * d6;
1758 if (vb <= 0 && d2 >= 0 && d6 <= 0) {
1759 mpq_class
w = d2 / (d2 - d6);
1761 mpq3
r =
a +
w * ac;
1764 if (dbg_level > 0) {
1765 std::cout <<
" answer = on ac at " <<
r <<
"\n";
1767 return mpq3::distance_squared(p,
r);
1770 mpq_class va = d3 * d6 - d5 * d4;
1771 if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
1772 mpq_class
w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
1779 if (dbg_level > 0) {
1780 std::cout <<
" answer = on bc at " <<
r <<
"\n";
1782 return mpq3::distance_squared(p,
r);
1785 mpq_class denom = 1 / (va + vb + vc);
1786 mpq_class
v = vb * denom;
1787 mpq_class
w = vc * denom;
1789 mpq3
r =
a +
v * ab;
1793 if (dbg_level > 0) {
1794 std::cout <<
" answer = inside at " <<
r <<
"\n";
1796 return mpq3::distance_squared(p,
r);
1799 struct ComponentContainer {
1800 int containing_component{NO_INDEX};
1801 int nearest_cell{NO_INDEX};
1802 mpq_class dist_to_cell;
1804 ComponentContainer(
int cc,
int cell, mpq_class d)
1805 : containing_component(cc), nearest_cell(cell), dist_to_cell(d)
1816 static Vector<ComponentContainer> find_component_containers(
int comp,
1817 const Vector<Vector<int>> &components,
1818 const Array<int> &ambient_cell,
1820 const PatchesInfo &pinfo,
1821 const TriMeshTopology &tmtopo,
1824 constexpr
int dbg_level = 0;
1825 if (dbg_level > 0) {
1826 std::cout <<
"FIND_COMPONENT_CONTAINERS for comp " << comp <<
"\n";
1828 Vector<ComponentContainer> ans;
1829 int test_p = components[comp][0];
1830 int test_t = pinfo.patch(test_p).tri(0);
1831 const Vert *test_v = tm.face(test_t)[0].vert[0];
1832 if (dbg_level > 0) {
1833 std::cout <<
"test vertex in comp: " << test_v <<
"\n";
1835 for (
int comp_other : components.index_range()) {
1836 if (comp == comp_other) {
1839 if (dbg_level > 0) {
1840 std::cout <<
"comp_other = " << comp_other <<
"\n";
1842 int nearest_tri = NO_INDEX;
1843 int nearest_tri_close_vert = -1;
1844 int nearest_tri_close_edge = -1;
1845 mpq_class nearest_tri_dist_squared;
1846 for (
int p : components[comp_other]) {
1847 const Patch &patch = pinfo.patch(p);
1848 for (
int t : patch.tris()) {
1849 const Face &tri = *tm.face(
t);
1850 if (dbg_level > 1) {
1851 std::cout <<
"tri " <<
t <<
" = " << &tri <<
"\n";
1855 mpq_class d2 = closest_on_tri_to_point(test_v->co_exact,
1861 if (dbg_level > 1) {
1862 std::cout <<
" close_edge=" << close_edge <<
" close_vert=" << close_vert
1863 <<
" dsquared=" << d2.get_d() <<
"\n";
1865 if (nearest_tri == NO_INDEX || d2 < nearest_tri_dist_squared) {
1867 nearest_tri_close_edge = close_edge;
1868 nearest_tri_close_vert = close_vert;
1869 nearest_tri_dist_squared = d2;
1873 if (dbg_level > 0) {
1874 std::cout <<
"closest tri to comp=" << comp <<
" in comp_other=" << comp_other <<
" is t"
1875 << nearest_tri <<
"\n";
1876 const Face *tn = tm.face(nearest_tri);
1877 std::cout <<
"tri = " << tn <<
"\n";
1878 std::cout <<
" (" << tn->vert[0]->co <<
"," << tn->vert[1]->co <<
"," << tn->vert[2]->co
1881 int containing_cell = find_containing_cell(test_v,
1883 nearest_tri_close_edge,
1884 nearest_tri_close_vert,
1890 if (dbg_level > 0) {
1891 std::cout <<
"containing cell = " << containing_cell <<
"\n";
1893 if (containing_cell != ambient_cell[comp_other]) {
1894 ans.append(ComponentContainer(comp_other, containing_cell, nearest_tri_dist_squared));
1907 static void finish_patch_cell_graph(
const IMesh &tm,
1910 const TriMeshTopology &tmtopo,
1913 constexpr
int dbg_level = 0;
1914 if (dbg_level > 0) {
1915 std::cout <<
"FINISH_PATCH_CELL_GRAPH\n";
1917 Vector<Vector<int>> components = find_patch_components(cinfo, pinfo);
1918 if (components.size() <= 1) {
1919 if (dbg_level > 0) {
1920 std::cout <<
"one component so finish_patch_cell_graph does no work\n";
1924 if (dbg_level > 0) {
1925 std::cout <<
"components:\n";
1926 for (
int comp : components.index_range()) {
1927 std::cout << comp <<
": " << components[comp] <<
"\n";
1930 Array<int> ambient_cell(components.size());
1931 for (
int comp : components.index_range()) {
1932 ambient_cell[comp] = find_ambient_cell(tm, &components[comp], tmtopo, pinfo, arena);
1934 if (dbg_level > 0) {
1935 std::cout <<
"ambient cells:\n";
1936 for (
int comp : ambient_cell.index_range()) {
1937 std::cout << comp <<
": " << ambient_cell[comp] <<
"\n";
1940 int tot_components = components.size();
1941 Array<Vector<ComponentContainer>> comp_cont(tot_components);
1942 for (
int comp : components.index_range()) {
1943 comp_cont[comp] = find_component_containers(
1944 comp, components, ambient_cell, tm, pinfo, tmtopo, arena);
1946 if (dbg_level > 0) {
1947 std::cout <<
"component containers:\n";
1948 for (
int comp : comp_cont.index_range()) {
1949 std::cout << comp <<
": ";
1950 for (
const ComponentContainer &cc : comp_cont[comp]) {
1951 std::cout <<
"[containing_comp=" << cc.containing_component
1952 <<
", nearest_cell=" << cc.nearest_cell <<
", d2=" << cc.dist_to_cell <<
"] ";
1957 if (dbg_level > 1) {
1958 write_obj_cell_patch(tm, cinfo, pinfo,
false,
"beforemerge");
1961 Vector<int> outer_components;
1962 for (
int comp : comp_cont.index_range()) {
1963 if (comp_cont[comp].
size() == 0) {
1964 outer_components.append(comp);
1967 ComponentContainer &
closest = comp_cont[comp][0];
1968 for (
int i = 1; i < comp_cont[comp].size(); ++i) {
1969 if (comp_cont[comp][i].dist_to_cell <
closest.dist_to_cell) {
1973 int comp_ambient = ambient_cell[comp];
1974 int cont_cell =
closest.nearest_cell;
1975 if (dbg_level > 0) {
1976 std::cout <<
"merge comp " << comp <<
"'s ambient cell=" << comp_ambient <<
" to cell "
1977 << cont_cell <<
"\n";
1979 merge_cells(cont_cell, comp_ambient, cinfo, pinfo);
1983 if (outer_components.size() > 1) {
1984 int merged_ambient = ambient_cell[outer_components[0]];
1985 for (
int i = 1; i < outer_components.size(); ++i) {
1986 if (dbg_level > 0) {
1987 std::cout <<
"merge comp " << outer_components[i]
1988 <<
"'s ambient cell=" << ambient_cell[outer_components[i]] <<
" to cell "
1989 << merged_ambient <<
"\n";
1991 merge_cells(merged_ambient, ambient_cell[outer_components[i]], cinfo, pinfo);
1994 if (dbg_level > 0) {
1995 std::cout <<
"after FINISH_PATCH_CELL_GRAPH\nCells\n";
1996 for (
int i : cinfo.index_range()) {
1997 if (cinfo.cell(i).merged_to() == NO_INDEX) {
1998 std::cout << i <<
": " << cinfo.cell(i) <<
"\n";
2001 std::cout <<
"Patches\n";
2002 for (
int i : pinfo.index_range()) {
2003 std::cout << i <<
": " << pinfo.patch(i) <<
"\n";
2005 if (dbg_level > 1) {
2006 write_obj_cell_patch(tm, cinfo, pinfo,
false,
"finished");
2024 static void propagate_windings_and_in_output_volume(PatchesInfo &pinfo,
2029 std::function<
int(
int)> shape_fn)
2032 if (dbg_level > 0) {
2033 std::cout <<
"PROPAGATE_WINDINGS, ambient cell = " << c_ambient <<
"\n";
2035 Cell &cell_ambient = cinfo.cell(c_ambient);
2036 cell_ambient.seed_ambient_winding();
2039 queue.reserve(cinfo.tot_cell());
2041 queue.append(c_ambient);
2042 while (queue_head <
queue.size()) {
2043 int c =
queue[queue_head++];
2044 if (dbg_level > 1) {
2045 std::cout <<
"process cell " <<
c <<
"\n";
2047 Cell &cell = cinfo.cell(
c);
2048 for (
int p : cell.patches()) {
2049 Patch &patch = pinfo.patch(p);
2050 bool p_above_c = patch.cell_below ==
c;
2051 int c_neighbor = p_above_c ? patch.cell_above : patch.cell_below;
2052 if (dbg_level > 1) {
2053 std::cout <<
" patch " << p <<
" p_above_c = " << p_above_c <<
"\n";
2054 std::cout <<
" c_neighbor = " << c_neighbor <<
"\n";
2056 Cell &cell_neighbor = cinfo.cell(c_neighbor);
2057 if (!cell_neighbor.winding_assigned()) {
2058 int winding_delta = p_above_c ? -1 : 1;
2059 int t = patch.tri(0);
2060 int shape = shape_fn(
t);
2063 if (dbg_level > 1) {
2064 std::cout <<
" representative tri " <<
t <<
": in shape " << shape <<
"\n";
2066 cell_neighbor.set_winding_and_in_output_volume(cell, shape, winding_delta, op);
2067 if (dbg_level > 1) {
2068 std::cout <<
" now cell_neighbor = " << cell_neighbor <<
"\n";
2070 queue.append(c_neighbor);
2075 if (dbg_level > 0) {
2076 std::cout <<
"\nPROPAGATE_WINDINGS result\n";
2077 for (
int i = 0; i < cinfo.tot_cell(); ++i) {
2078 std::cout << i <<
": " << cinfo.cell(i) <<
"\n";
2092 static bool apply_bool_op(BoolOpType bool_optype,
const Array<int> &winding)
2094 int nw = winding.size();
2096 switch (bool_optype) {
2098 for (
int i = 0; i < nw; ++i) {
2099 if (winding[i] == 0) {
2105 case BoolOpType::Union: {
2106 for (
int i = 0; i < nw; ++i) {
2107 if (winding[i] != 0) {
2113 case BoolOpType::Difference: {
2115 if (winding[0] == 0) {
2121 for (
int i = 1; i < nw; ++i) {
2122 if (winding[i] >= 1) {
2138 static void extract_zero_volume_cell_tris(Vector<Face *> &r_tris,
2139 const IMesh &tm_subdivided,
2140 const PatchesInfo &pinfo,
2141 const CellsInfo &cinfo,
2145 if (dbg_level > 0) {
2146 std::cout <<
"extract_zero_volume_cell_tris\n";
2149 Array<bool> adj_to_zv(pinfo.tot_patch());
2150 for (
int p : pinfo.index_range()) {
2151 const Patch &patch = pinfo.patch(p);
2152 if (cinfo.cell(patch.cell_above).zero_volume() || cinfo.cell(patch.cell_below).zero_volume()) {
2153 adj_to_zv[p] =
true;
2156 adj_to_zv[p] =
false;
2160 Vector<Vector<int>> patch_stacks;
2161 Array<bool> allocated_to_stack(pinfo.tot_patch(),
false);
2162 for (
int p : pinfo.index_range()) {
2163 if (!adj_to_zv[p] || allocated_to_stack[p]) {
2166 int stack_index = patch_stacks.size();
2167 patch_stacks.append(Vector<int>{p});
2168 Vector<int> &stack = patch_stacks[stack_index];
2169 Vector<bool> flipped{
false};
2170 allocated_to_stack[p] =
true;
2177 const Patch *pwalk_patch = &pinfo.patch(pwalk);
2178 int c = pwalk_patch->cell_above;
2179 const Cell *cell = &cinfo.cell(
c);
2180 while (cell->zero_volume()) {
2183 int pother = cell->patch_other(pwalk);
2184 bool flip = pinfo.patch(pother).cell_above ==
c;
2185 flipped.append(flip);
2186 stack.append(pother);
2187 allocated_to_stack[pother] =
true;
2189 pwalk_patch = &pinfo.patch(pwalk);
2190 c = flip ? pwalk_patch->cell_below : pwalk_patch->cell_above;
2191 cell = &cinfo.cell(
c);
2193 const Cell *above_stack_cell = cell;
2196 pwalk_patch = &pinfo.patch(pwalk);
2197 c = pwalk_patch->cell_below;
2198 cell = &cinfo.cell(
c);
2199 while (cell->zero_volume()) {
2201 int pother = cell->patch_other(pwalk);
2202 bool flip = pinfo.patch(pother).cell_below ==
c;
2203 flipped.append(flip);
2204 stack.append(pother);
2205 allocated_to_stack[pother] =
true;
2207 pwalk_patch = &pinfo.patch(pwalk);
2208 c = flip ? pwalk_patch->cell_above : pwalk_patch->cell_below;
2209 cell = &cinfo.cell(
c);
2211 const Cell *below_stack_cell = cell;
2212 if (dbg_level > 0) {
2213 std::cout <<
"process zero-volume patch stack " << stack <<
"\n";
2214 std::cout <<
"flipped = ";
2215 for (
bool b : flipped) {
2216 std::cout << b <<
" ";
2220 if (above_stack_cell->in_output_volume() ^ below_stack_cell->in_output_volume()) {
2221 bool need_flipped_tri = above_stack_cell->in_output_volume();
2222 if (dbg_level > 0) {
2223 std::cout <<
"need tri " << (need_flipped_tri ?
"flipped" :
"") <<
"\n";
2225 int t_to_add = NO_INDEX;
2226 for (
int i : stack.index_range()) {
2227 if (flipped[i] == need_flipped_tri) {
2228 t_to_add = pinfo.patch(stack[i]).tri(0);
2229 if (dbg_level > 0) {
2230 std::cout <<
"using tri " << t_to_add <<
"\n";
2232 r_tris.append(tm_subdivided.face(t_to_add));
2236 if (t_to_add == NO_INDEX) {
2237 const Face *f = tm_subdivided.face(pinfo.patch(p).tri(0));
2238 const Face &tri = *f;
2240 std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]};
2241 std::array<int, 3> flipped_e_origs = {
2242 tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
2243 std::array<bool, 3> flipped_is_intersect = {
2244 tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
2245 Face *flipped_f = arena->add_face(
2246 flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect);
2247 r_tris.append(flipped_f);
2264 static IMesh extract_from_in_output_volume_diffs(
const IMesh &tm_subdivided,
2265 const PatchesInfo &pinfo,
2266 const CellsInfo &cinfo,
2269 constexpr
int dbg_level = 0;
2270 if (dbg_level > 0) {
2271 std::cout <<
"\nEXTRACT_FROM_FLAG_DIFFS\n";
2273 Vector<Face *> out_tris;
2274 out_tris.reserve(tm_subdivided.face_size());
2275 bool any_zero_volume_cell =
false;
2276 for (
int t : tm_subdivided.face_index_range()) {
2277 int p = pinfo.tri_patch(
t);
2278 const Patch &patch = pinfo.patch(p);
2279 const Cell &cell_above = cinfo.cell(patch.cell_above);
2280 const Cell &cell_below = cinfo.cell(patch.cell_below);
2281 if (dbg_level > 0) {
2282 std::cout <<
"tri " <<
t <<
": cell_above=" << patch.cell_above
2283 <<
" cell_below=" << patch.cell_below <<
"\n";
2284 std::cout <<
" in_output_volume_above=" << cell_above.in_output_volume()
2285 <<
" in_output_volume_below=" << cell_below.in_output_volume() <<
"\n";
2287 bool adjacent_zero_volume_cell = cell_above.zero_volume() || cell_below.zero_volume();
2288 any_zero_volume_cell |= adjacent_zero_volume_cell;
2289 if (cell_above.in_output_volume() ^ cell_below.in_output_volume() &&
2290 !adjacent_zero_volume_cell) {
2291 bool flip = cell_above.in_output_volume();
2292 if (dbg_level > 0) {
2293 std::cout <<
"need tri " <<
t <<
" flip=" << flip <<
"\n";
2295 Face *f = tm_subdivided.face(
t);
2298 std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]};
2299 std::array<int, 3> flipped_e_origs = {
2300 tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
2301 std::array<bool, 3> flipped_is_intersect = {
2302 tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
2303 Face *flipped_f = arena->add_face(
2304 flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect);
2305 out_tris.append(flipped_f);
2312 if (any_zero_volume_cell) {
2313 extract_zero_volume_cell_tris(out_tris, tm_subdivided, pinfo, cinfo, arena);
2315 return IMesh(out_tris);
2318 static const char *bool_optype_name(BoolOpType op)
2321 case BoolOpType::None:
2325 case BoolOpType::Union:
2327 case BoolOpType::Difference:
2328 return "difference";
2334 static double3 calc_point_inside_tri_db(
const Face &tri)
2336 const Vert *v0 = tri.vert[0];
2337 const Vert *
v1 = tri.vert[1];
2338 const Vert *
v2 = tri.vert[2];
2339 double3 ans = v0->co / 3 +
v1->co / 3 +
v2->
co / 3;
2342 class InsideShapeTestData {
2345 std::function<int(
int)> shape_fn;
2348 Array<int> hit_parity;
2350 InsideShapeTestData(
const IMesh &tm, std::function<
int(
int)> shape_fn,
int nshapes)
2351 : tm(tm), shape_fn(shape_fn), nshapes(nshapes)
2356 static void inside_shape_callback(
void *userdata,
2361 const int dbg_level = 0;
2362 if (dbg_level > 0) {
2363 std::cout <<
"inside_shape_callback, index = " << index <<
"\n";
2365 InsideShapeTestData *
data =
static_cast<InsideShapeTestData *
>(userdata);
2366 const Face &tri = *
data->tm.face(index);
2367 int shape =
data->shape_fn(tri.orig);
2375 for (
int i = 0; i < 3; ++i) {
2376 fv0[i] =
float(tri.vert[0]->co[i]);
2377 fv1[i] =
float(tri.vert[1]->co[i]);
2378 fv2[i] =
float(tri.vert[2]->co[i]);
2380 if (dbg_level > 0) {
2381 std::cout <<
" fv0=(" << fv0[0] <<
"," << fv0[1] <<
"," << fv0[2] <<
")\n";
2382 std::cout <<
" fv1=(" << fv1[0] <<
"," << fv1[1] <<
"," << fv1[2] <<
")\n";
2383 std::cout <<
" fv2=(" << fv2[0] <<
"," << fv2[1] <<
"," << fv2[2] <<
")\n";
2386 ray->
origin, ray->
direction, fv0, fv1, fv2, &dist,
nullptr, FLT_EPSILON)) {
2390 int parity =
orient3d(tri.vert[0]->co, tri.vert[1]->co, tri.vert[2]->co, o_db);
2391 if (dbg_level > 0) {
2392 std::cout <<
"origin at " << o_db <<
", parity = " << parity <<
"\n";
2394 data->hit_parity[shape] += parity;
2407 static void test_tri_inside_shapes(
const IMesh &tm,
2408 std::function<
int(
int)> shape_fn,
2412 Array<float> &in_shape)
2414 const int dbg_level = 0;
2415 if (dbg_level > 0) {
2416 std::cout <<
"test_point_inside_shapes, t_index = " << test_t_index <<
"\n";
2418 Face &tri_test = *tm.face(test_t_index);
2419 int shape = shape_fn(tri_test.orig);
2421 in_shape.fill(0.0f);
2424 double3 test_point = calc_point_inside_tri_db(tri_test);
2426 tri_test.populate_plane(
false);
2427 double3
norm = tri_test.plane->norm.normalized();
2428 const double offset_amount = 1
e-5;
2429 double3 offset_test_point = test_point + offset_amount *
norm;
2430 if (dbg_level > 0) {
2431 std::cout <<
"test tri is in shape " << shape <<
"\n";
2432 std::cout <<
"test point = " << test_point <<
"\n";
2433 std::cout <<
"offset_test_point = " << offset_test_point <<
"\n";
2439 constexpr
int num_rays = 6;
2440 constexpr
float r1 = 0.9987025295199663f;
2441 constexpr
float ra = 0.04993512647599832f;
2442 constexpr
float rb = 0.009987025295199663f;
2443 const float test_rays[num_rays][3] = {
2444 {r1, ra, rb}, {-r1, -ra, -rb}, {rb, r1, ra}, {-rb, -r1, -ra}, {ra, rb, r1}, {-ra, -rb, -r1}};
2445 InsideShapeTestData
data(tm, shape_fn, nshapes);
2446 data.hit_parity = Array<int>(nshapes, 0);
2447 Array<int> count_insides(nshapes, 0);
2448 const float co[3] = {
2449 float(offset_test_point[0]),
float(offset_test_point[1]),
float(offset_test_point[2])};
2450 for (
int i = 0; i < num_rays; ++i) {
2451 if (dbg_level > 0) {
2452 std::cout <<
"shoot ray " << i <<
"(" << test_rays[i][0] <<
"," << test_rays[i][1] <<
","
2453 << test_rays[i][2] <<
")\n";
2456 if (dbg_level > 0) {
2457 std::cout <<
"ray " << i <<
" result:";
2458 for (
int j = 0; j < nshapes; ++j) {
2459 std::cout <<
" " <<
data.hit_parity[j];
2463 for (
int j = 0; j < nshapes; ++j) {
2464 if (j != shape &&
data.hit_parity[j] > 0) {
2468 data.hit_parity.fill(0);
2470 for (
int j = 0; j < nshapes; ++j) {
2475 in_shape[j] =
float(count_insides[j]) /
float(num_rays);
2477 if (dbg_level > 0) {
2478 std::cout <<
"shape " << j <<
" inside = " << in_shape[j] <<
"\n";
2489 static BVHTree *raycast_tree(
const IMesh &tm)
2492 for (
int i : tm.face_index_range()) {
2493 const Face *f = tm.face(i);
2495 for (
int j = 0; j < 3; ++j) {
2496 const Vert *
v = f->vert[j];
2497 for (
int k = 0; k < 3; ++k) {
2498 t_cos[3 * j + k] =
float(
v->
co[k]);
2511 static bool raycast_test_remove(BoolOpType op, Array<int> &winding,
int shape,
bool *r_do_flip)
2513 constexpr
int dbg_level = 0;
2519 bool in_output_volume_0 = apply_bool_op(op, winding);
2521 bool in_output_volume_1 = apply_bool_op(op, winding);
2522 bool do_remove = in_output_volume_0 == in_output_volume_1;
2523 bool do_flip = !do_remove && op == BoolOpType::Difference && shape != 0;
2524 if (dbg_level > 0) {
2525 std::cout <<
"winding = ";
2526 for (
int i = 0; i < winding.size(); ++i) {
2527 std::cout << winding[i] <<
" ";
2529 std::cout <<
"\niv0=" << in_output_volume_0 <<
", iv1=" << in_output_volume_1 <<
"\n";
2530 std::cout <<
" remove=" << do_remove <<
", flip=" << do_flip <<
"\n";
2532 *r_do_flip = do_flip;
2537 static void raycast_add_flipped(Vector<Face *> &out_faces,
Face &tri, IMeshArena *arena)
2540 Array<const Vert *> flipped_vs = {tri[0], tri[2], tri[1]};
2541 Array<int> flipped_e_origs = {tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]};
2542 Array<bool> flipped_is_intersect = {
2543 tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]};
2544 Face *flipped_f = arena->add_face(flipped_vs, tri.orig, flipped_e_origs, flipped_is_intersect);
2545 out_faces.append(flipped_f);
2556 static IMesh raycast_tris_boolean(
const IMesh &tm,
2559 std::function<
int(
int)> shape_fn,
2562 constexpr
int dbg_level = 0;
2563 if (dbg_level > 0) {
2564 std::cout <<
"RAYCAST_TRIS_BOOLEAN\n";
2568 Vector<Face *> out_faces;
2569 out_faces.reserve(tm.face_size());
2570 Array<float> in_shape(nshapes, 0);
2571 Array<int> winding(nshapes, 0);
2572 for (
int t : tm.face_index_range()) {
2573 Face &tri = *tm.face(
t);
2574 int shape = shape_fn(tri.orig);
2575 if (dbg_level > 0) {
2576 std::cout <<
"process triangle " <<
t <<
" = " << &tri <<
"\n";
2577 std::cout <<
"shape = " << shape <<
"\n";
2579 test_tri_inside_shapes(tm, shape_fn, nshapes,
t,
tree, in_shape);
2580 for (
int other_shape = 0; other_shape < nshapes; ++other_shape) {
2581 if (other_shape == shape) {
2591 bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
2593 bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
2594 if (dbg_level > 0) {
2595 std::cout <<
"test point is " << (inside ?
"inside" :
"outside") <<
" other_shape "
2596 << other_shape <<
" val = " << in_shape[other_shape] <<
"\n";
2598 winding[other_shape] = inside;
2601 bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
2604 out_faces.append(&tri);
2607 raycast_add_flipped(out_faces, tri, arena);
2612 ans.set_faces(out_faces);
2620 static IMesh raycast_patches_boolean(
const IMesh &tm,
2623 std::function<
int(
int)> shape_fn,
2624 const PatchesInfo &pinfo,
2627 constexpr
int dbg_level = 0;
2628 if (dbg_level > 0) {
2629 std::cout <<
"RAYCAST_PATCHES_BOOLEAN\n";
2633 Vector<Face *> out_faces;
2634 out_faces.reserve(tm.face_size());
2635 Array<float> in_shape(nshapes, 0);
2636 Array<int> winding(nshapes, 0);
2637 for (
int p : pinfo.index_range()) {
2638 const Patch &patch = pinfo.patch(p);
2641 int test_t_index = patch.tri(patch.tot_tri() / 2);
2642 Face &tri_test = *tm.face(test_t_index);
2644 int shape = shape_fn(tri_test.orig);
2645 if (dbg_level > 0) {
2646 std::cout <<
"process patch " << p <<
" = " << patch <<
"\n";
2647 std::cout <<
"test tri = " << test_t_index <<
" = " << &tri_test <<
"\n";
2648 std::cout <<
"shape = " << shape <<
"\n";
2653 test_tri_inside_shapes(tm, shape_fn, nshapes, test_t_index,
tree, in_shape);
2654 for (
int other_shape = 0; other_shape < nshapes; ++other_shape) {
2655 if (other_shape == shape) {
2658 bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
2660 bool inside = in_shape[other_shape] >= (need_high_confidence ? 0.5f : 0.1f);
2661 if (dbg_level > 0) {
2662 std::cout <<
"test point is " << (inside ?
"inside" :
"outside") <<
" other_shape "
2663 << other_shape <<
" val = " << in_shape[other_shape] <<
"\n";
2665 winding[other_shape] = inside;
2668 bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
2670 for (
int t : patch.tris()) {
2671 Face *f = tm.face(
t);
2673 out_faces.append(f);
2676 raycast_add_flipped(out_faces, *f, arena);
2683 ans.set_faces(out_faces);
2690 static std::pair<int, int> find_tris_common_edge(
const Face &tri1,
const Face &tri2)
2692 for (
int i = 0; i < 3; ++i) {
2693 for (
int j = 0; j < 3; ++j) {
2694 if (tri1[(i + 1) % 3] == tri2[j] && tri1[i] == tri2[(j + 1) % 3]) {
2695 return std::pair<int, int>(i, j);
2699 return std::pair<int, int>(-1, -1);
2706 const Vert *
v1 =
nullptr;
2707 const Vert *
v2 =
nullptr;
2710 int right_face = -1;
2713 bool dissolvable =
false;
2715 bool is_intersect =
false;
2717 MergeEdge() =
default;
2719 MergeEdge(
const Vert *va,
const Vert *vb)
2721 if (va->id < vb->id) {
2734 Vector<const Vert *> vert;
2742 struct FaceMergeState {
2746 Vector<MergeFace> face;
2751 Vector<MergeEdge> edge;
2756 Map<std::pair<int, int>,
int> edge_map;
2759 static std::ostream &
operator<<(std::ostream &os,
const FaceMergeState &fms)
2762 for (
int f : fms.face.index_range()) {
2763 const MergeFace &mf = fms.face[f];
2764 std::cout << f <<
": orig=" << mf.orig <<
" verts ";
2765 for (
const Vert *
v : mf.vert) {
2766 std::cout <<
v <<
" ";
2769 std::cout <<
" edges " << mf.edge <<
"\n";
2770 std::cout <<
" merge_to = " << mf.merge_to <<
"\n";
2773 for (
int e : fms.edge.index_range()) {
2774 const MergeEdge &me = fms.edge[
e];
2775 std::cout <<
e <<
": (" << me.v1 <<
"," << me.v2 <<
") left=" << me.left_face
2776 <<
" right=" << me.right_face <<
" dis=" << me.dissolvable <<
" orig=" << me.orig
2777 <<
" is_int=" << me.is_intersect <<
"\n";
2787 static void init_face_merge_state(FaceMergeState *fms,
2788 const Vector<int> &tris,
2790 const double3 &
norm)
2792 constexpr
int dbg_level = 0;
2794 fms->face.reserve(tris.size() + 1);
2795 fms->edge.reserve(3 * tris.size());
2796 fms->edge_map.reserve(3 * tris.size());
2797 if (dbg_level > 0) {
2798 std::cout <<
"\nINIT_FACE_MERGE_STATE\n";
2800 for (
int t : tris.index_range()) {
2802 const Face &tri = *tm.face(tris[
t]);
2803 if (dbg_level > 0) {
2804 std::cout <<
"process tri = " << &tri <<
"\n";
2808 if (dbg_level > 0) {
2809 std::cout <<
"triangle has wrong orientation, skipping\n";
2813 mf.vert.append(tri[0]);
2814 mf.vert.append(tri[1]);
2815 mf.vert.append(tri[2]);
2817 int f = fms->face.append_and_get_index(mf);
2818 if (dbg_level > 1) {
2819 std::cout <<
"appended MergeFace for tri at f = " << f <<
"\n";
2821 for (
int i = 0; i < 3; ++i) {
2822 int inext = (i + 1) % 3;
2823 MergeEdge new_me(mf.vert[i], mf.vert[inext]);
2824 std::pair<int, int> canon_vs(new_me.v1->id, new_me.v2->id);
2825 int me_index = fms->edge_map.lookup_default(canon_vs, -1);
2826 if (dbg_level > 1) {
2827 std::cout <<
"new_me = canon_vs = " << new_me.v1 <<
", " << new_me.v2 <<
"\n";
2828 std::cout <<
"me_index lookup = " << me_index <<
"\n";
2830 if (me_index == -1) {
2831 double3 vec = new_me.v2->co - new_me.v1->co;
2832 new_me.len_squared = vec.length_squared();
2833 new_me.orig = tri.edge_orig[i];
2834 new_me.is_intersect = tri.is_intersect[i];
2835 new_me.dissolvable = (new_me.orig == NO_INDEX && !new_me.is_intersect);
2836 fms->edge.append(new_me);
2837 me_index = fms->edge.size() - 1;
2838 fms->edge_map.add_new(canon_vs, me_index);
2839 if (dbg_level > 1) {
2840 std::cout <<
"added new me with me_index = " << me_index <<
"\n";
2841 std::cout <<
" len_squared = " << new_me.len_squared <<
" orig = " << new_me.orig
2842 <<
", is_intersect" << new_me.is_intersect
2843 <<
", dissolvable = " << new_me.dissolvable <<
"\n";
2846 MergeEdge &me = fms->edge[me_index];
2847 if (dbg_level > 1) {
2848 std::cout <<
"retrieved me at index " << me_index <<
":\n";
2849 std::cout <<
" v1 = " << me.v1 <<
" v2 = " << me.v2 <<
"\n";
2850 std::cout <<
" dis = " << me.dissolvable <<
" int = " << me.is_intersect <<
"\n";
2851 std::cout <<
" left_face = " << me.left_face <<
" right_face = " << me.right_face <<
"\n";
2853 if (me.dissolvable && tri.edge_orig[i] != NO_INDEX) {
2854 if (dbg_level > 1) {
2855 std::cout <<
"reassigning orig to " << tri.edge_orig[i] <<
", dissolvable = false\n";
2857 me.dissolvable =
false;
2858 me.orig = tri.edge_orig[i];
2860 if (me.dissolvable && tri.is_intersect[i]) {
2861 if (dbg_level > 1) {
2862 std::cout <<
"reassigning dissolvable = false, is_intersect = true\n";
2864 me.dissolvable =
false;
2865 me.is_intersect =
true;
2868 if (me.v1 == mf.vert[i]) {
2869 if (dbg_level > 1) {
2870 std::cout <<
"me.v1 == mf.vert[i] so set edge[" << me_index <<
"].left_face = " << f
2874 fms->edge[me_index].left_face = f;
2877 if (dbg_level > 1) {
2878 std::cout <<
"me.v1 != mf.vert[i] so set edge[" << me_index <<
"].right_face = " << f
2882 fms->edge[me_index].right_face = f;
2884 fms->face[f].edge.append(me_index);
2887 if (dbg_level > 0) {
2898 static bool dissolve_leaves_valid_bmesh(FaceMergeState *fms,
2899 const MergeEdge &me,
2901 const MergeFace &mf_left,
2902 const MergeFace &mf_right)
2904 int a_edge_start = mf_left.edge.first_index_of_try(me_index);
2906 int alen = mf_left.vert.size();
2907 int blen = mf_right.vert.size();
2908 int b_left_face = me.right_face;
2911 for (
int a_e_index = (a_edge_start + 1) % alen; ok && a_e_index != a_edge_start;
2912 a_e_index = (a_e_index + 1) % alen) {
2913 const MergeEdge &a_me_cur = fms->edge[mf_left.edge[a_e_index]];
2914 if (a_me_cur.right_face == b_left_face) {
2921 for (
int a_v_index = 0; ok && a_v_index < alen; ++a_v_index) {
2922 const Vert *a_v = mf_left.vert[a_v_index];
2923 if (!
ELEM(a_v, me.v1, me.v2)) {
2924 for (
int b_v_index = 0; b_v_index < blen; ++b_v_index) {
2925 const Vert *b_v = mf_right.vert[b_v_index];
2942 static void splice_faces(
2943 FaceMergeState *fms, MergeEdge &me,
int me_index, MergeFace &mf_left, MergeFace &mf_right)
2945 int a_edge_start = mf_left.edge.first_index_of_try(me_index);
2946 int b_edge_start = mf_right.edge.first_index_of_try(me_index);
2947 BLI_assert(a_edge_start != -1 && b_edge_start != -1);
2948 int alen = mf_left.vert.size();
2949 int blen = mf_right.vert.size();
2950 Vector<const Vert *> splice_vert;
2951 Vector<int> splice_edge;
2952 splice_vert.reserve(alen + blen - 2);
2953 splice_edge.reserve(alen + blen - 2);
2955 while (ai < a_edge_start) {
2956 splice_vert.append(mf_left.vert[ai]);
2957 splice_edge.append(mf_left.edge[ai]);
2960 int bi = b_edge_start + 1;
2961 while (bi != b_edge_start) {
2964 if (bi == b_edge_start) {
2968 splice_vert.append(mf_right.vert[bi]);
2969 splice_edge.append(mf_right.edge[bi]);
2970 if (mf_right.vert[bi] == fms->edge[mf_right.edge[bi]].v1) {
2971 fms->edge[mf_right.edge[bi]].left_face = me.left_face;
2974 fms->edge[mf_right.edge[bi]].right_face = me.left_face;
2978 ai = a_edge_start + 1;
2980 splice_vert.append(mf_left.vert[ai]);
2981 splice_edge.append(mf_left.edge[ai]);
2984 mf_right.merge_to = me.left_face;
2985 mf_left.vert = splice_vert;
2986 mf_left.edge = splice_edge;
2998 static void do_dissolve(FaceMergeState *fms)
3000 const int dbg_level = 0;
3001 if (dbg_level > 1) {
3002 std::cout <<
"\nDO_DISSOLVE\n";
3004 Vector<int> dissolve_edges;
3005 for (
int e : fms->edge.index_range()) {
3006 if (fms->edge[
e].dissolvable) {
3007 dissolve_edges.append(
e);
3010 if (dissolve_edges.size() == 0) {
3015 dissolve_edges.begin(), dissolve_edges.end(), [fms](
const int &
a,
const int &b) ->
bool {
3016 return (fms->edge[a].len_squared > fms->edge[b].len_squared);
3018 if (dbg_level > 0) {
3019 std::cout <<
"Sorted dissolvable edges: " << dissolve_edges <<
"\n";
3021 for (
int me_index : dissolve_edges) {
3022 MergeEdge &me = fms->edge[me_index];
3023 if (me.left_face == -1 || me.right_face == -1) {
3026 MergeFace &mf_left = fms->face[me.left_face];
3027 MergeFace &mf_right = fms->face[me.right_face];
3028 if (!dissolve_leaves_valid_bmesh(fms, me, me_index, mf_left, mf_right)) {
3031 if (dbg_level > 0) {
3032 std::cout <<
"Removing edge " << me_index <<
"\n";
3034 splice_faces(fms, me, me_index, mf_left, mf_right);
3035 if (dbg_level > 1) {
3036 std::cout <<
"state after removal:\n";
3052 static Vector<Face *> merge_tris_for_face(Vector<int> tris,
3054 const IMesh &imesh_in,
3057 constexpr
int dbg_level = 0;
3058 if (dbg_level > 0) {
3059 std::cout <<
"merge_tris_for_face\n";
3060 std::cout <<
"tris: " << tris <<
"\n";
3063 if (tris.size() <= 1) {
3064 if (tris.size() == 1) {
3065 ans.append(tm.face(tris[0]));
3070 double3 first_tri_normal = tm.face(tris[0])->plane->norm;
3071 double3 second_tri_normal = tm.face(tris[1])->plane->norm;
3072 if (tris.size() == 2 &&
double3::dot(first_tri_normal, second_tri_normal) > 0.0) {
3075 Face &tri1 = *tm.face(tris[0]);
3076 Face &tri2 = *tm.face(tris[1]);
3077 Face *in_face = imesh_in.face(tri1.orig);
3078 if (in_face->size() == 4) {
3079 std::pair<int, int> estarts = find_tris_common_edge(tri1, tri2);
3080 if (estarts.first != -1 && tri1.edge_orig[estarts.first] == NO_INDEX) {
3081 if (dbg_level > 0) {
3082 std::cout <<
"try recovering orig quad case\n";
3083 std::cout <<
"tri1 = " << &tri1 <<
"\n";
3084 std::cout <<
"tri1 = " << &tri2 <<
"\n";
3086 int i0 = estarts.first;
3087 int i1 = (i0 + 1) % 3;
3088 int i2 = (i0 + 2) % 3;
3089 int j2 = (estarts.second + 2) % 3;
3090 Face tryface({tri1[
i1], tri1[i2], tri1[i0], tri2[j2]}, -1, -1, {}, {});
3091 if (tryface.cyclic_equal(*in_face)) {
3092 if (dbg_level > 0) {
3093 std::cout <<
"inface = " << in_face <<
"\n";
3094 std::cout <<
"quad recovery worked\n";
3096 ans.append(in_face);
3106 double3 first_tri_normal_rev = -first_tri_normal;
3107 for (
const double3 &
norm : {first_tri_normal, first_tri_normal_rev}) {
3109 init_face_merge_state(&fms, tris, tm,
norm);
3111 if (dbg_level > 0) {
3112 std::cout <<
"faces in merged result:\n";
3114 for (
const MergeFace &mf : fms.face) {
3115 if (mf.merge_to == -1) {
3116 Array<int> e_orig(mf.edge.size());
3117 Array<bool> is_intersect(mf.edge.size());
3118 for (
int i : mf.edge.index_range()) {
3119 e_orig[i] = fms.edge[mf.edge[i]].orig;
3120 is_intersect[i] = fms.edge[mf.edge[i]].is_intersect;
3122 Face *facep = arena->add_face(mf.vert, mf.orig, e_orig, is_intersect);
3124 if (dbg_level > 0) {
3125 std::cout <<
" " << facep <<
"\n";
3133 static bool approx_in_line(
const double3 &
a,
const double3 &b,
const double3 &
c)
3135 double3 vec1 = b -
a;
3136 double3 vec2 =
c - b;
3137 double cos_ang =
double3::dot(vec1.normalized(), vec2.normalized());
3138 return fabs(cos_ang - 1.0) < 1
e-4;
3147 static Array<bool> find_dissolve_verts(IMesh &imesh_out,
int *r_count_dissolve)
3149 imesh_out.populate_vert();
3151 Array<bool> dissolve(imesh_out.vert_size());
3152 for (
int v_index : imesh_out.vert_index_range()) {
3153 const Vert &vert = *imesh_out.vert(v_index);
3154 dissolve[v_index] = (vert.orig == NO_INDEX);
3159 Array<std::pair<const Vert *, const Vert *>> neighbors(
3160 imesh_out.vert_size(), std::pair<const Vert *, const Vert *>(
nullptr,
nullptr));
3161 for (
int f : imesh_out.face_index_range()) {
3162 const Face &face = *imesh_out.face(f);
3163 for (
int i : face.index_range()) {
3164 const Vert *
v = face[i];
3165 int v_index = imesh_out.lookup_vert(
v);
3167 if (dissolve[v_index]) {
3168 const Vert *n1 = face[face.next_pos(i)];
3169 const Vert *n2 = face[face.prev_pos(i)];
3170 const Vert *f_n1 = neighbors[v_index].first;
3171 const Vert *f_n2 = neighbors[v_index].second;
3172 if (f_n1 !=
nullptr) {
3174 if (!((n1 == f_n2 && n2 == f_n1) || (n1 == f_n1 && n2 == f_n2))) {
3176 dissolve[v_index] =
false;
3181 neighbors[v_index] = std::pair<const Vert *, const Vert *>(n1, n2);
3187 for (
int v_out : imesh_out.vert_index_range()) {
3188 if (dissolve[v_out]) {
3189 dissolve[v_out] =
false;
3190 const std::pair<const Vert *, const Vert *> &nbrs = neighbors[v_out];
3191 if (nbrs.first !=
nullptr) {
3193 const Vert *v_v_out = imesh_out.vert(v_out);
3194 if (approx_in_line(nbrs.first->co, v_v_out->co, nbrs.second->co)) {
3195 dissolve[v_out] =
true;
3201 if (r_count_dissolve !=
nullptr) {
3202 *r_count_dissolve =
count;
3212 static void dissolve_verts(IMesh *imesh,
const Array<bool> dissolve, IMeshArena *arena)
3214 constexpr
int inline_face_size = 100;
3215 Vector<bool, inline_face_size> face_pos_erase;
3216 bool any_faces_erased =
false;
3217 for (
int f : imesh->face_index_range()) {
3218 const Face &face = *imesh->face(f);
3219 face_pos_erase.clear();
3221 for (
const Vert *
v : face) {
3222 int v_index = imesh->lookup_vert(
v);
3224 if (dissolve[v_index]) {
3225 face_pos_erase.append(
true);
3229 face_pos_erase.append(
false);
3232 if (num_erase > 0) {
3233 any_faces_erased |= imesh->erase_face_positions(f, face_pos_erase, arena);
3236 imesh->set_dirty_verts();
3237 if (any_faces_erased) {
3238 imesh->remove_null_faces();
3253 static IMesh polymesh_from_trimesh_with_dissolve(
const IMesh &tm_out,
3254 const IMesh &imesh_in,
3257 const int dbg_level = 0;
3258 if (dbg_level > 1) {
3259 std::cout <<
"\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n";
3262 for (
Face *tri : tm_out.faces()) {
3263 tri->populate_plane(
false);
3268 int tot_in_face = imesh_in.face_size();
3269 Array<Vector<int>> face_output_tris(tot_in_face);
3270 for (
int t : tm_out.face_index_range()) {
3271 const Face &tri = *tm_out.face(
t);
3272 int in_face = tri.orig;
3273 face_output_tris[in_face].append(
t);
3275 if (dbg_level > 1) {
3276 std::cout <<
"face_output_tris:\n";
3277 for (
int f : face_output_tris.index_range()) {
3278 std::cout << f <<
": " << face_output_tris[f] <<
"\n";
3285 Array<Vector<Face *>> face_output_face(tot_in_face);
3286 int tot_out_face = 0;
3287 for (
int in_f : imesh_in.face_index_range()) {
3288 if (dbg_level > 1) {
3289 std::cout <<
"merge tris for face " << in_f <<
"\n";
3291 int num_out_tris_for_face = face_output_tris.size();
3292 if (num_out_tris_for_face == 0) {
3295 face_output_face[in_f] = merge_tris_for_face(face_output_tris[in_f], tm_out, imesh_in, arena);
3296 tot_out_face += face_output_face[in_f].size();
3298 Array<Face *> face(tot_out_face);
3299 int out_f_index = 0;
3300 for (
int in_f : imesh_in.face_index_range()) {
3301 const Vector<Face *> &f_faces = face_output_face[in_f];
3302 if (f_faces.size() > 0) {
3303 std::copy(f_faces.begin(), f_faces.end(), &face[out_f_index]);
3304 out_f_index += f_faces.size();
3307 IMesh imesh_out(face);
3312 Array<bool> v_dissolve = find_dissolve_verts(imesh_out, &count_dissolve);
3313 if (count_dissolve > 0) {
3314 dissolve_verts(&imesh_out, v_dissolve, arena);
3316 if (dbg_level > 1) {
3317 write_obj_mesh(imesh_out,
"boolean_post_dissolve");
3329 IMesh boolean_trimesh(IMesh &tm_in,
3332 std::function<
int(
int)> shape_fn,
3337 constexpr
int dbg_level = 0;
3338 if (dbg_level > 0) {
3339 std::cout <<
"BOOLEAN of " << nshapes <<
" operand" << (nshapes == 1 ?
"" :
"s")
3340 <<
" op=" << bool_optype_name(op) <<
"\n";
3341 if (dbg_level > 1) {
3342 tm_in.populate_vert();
3343 std::cout <<
"boolean_trimesh input:\n" << tm_in;
3344 write_obj_mesh(tm_in,
"boolean_in");
3347 if (tm_in.face_size() == 0) {
3348 return IMesh(tm_in);
3352 std::cout <<
" boolean_trimesh, timing begins\n";
3355 IMesh tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena);
3356 if (dbg_level > 1) {
3357 write_obj_mesh(tm_si,
"boolean_tm_si");
3358 std::cout <<
"\nboolean_tm_input after intersection:\n" << tm_si;
3362 std::cout <<
" intersected, time = " << intersect_time - start_time <<
"\n";
3366 if (tm_si.face_size() == 0 || op == BoolOpType::None) {
3369 auto si_shape_fn = [shape_fn, tm_si](
int t) {
return shape_fn(tm_si.face(
t)->orig); };
3370 TriMeshTopology tm_si_topo(tm_si);
3373 std::cout <<
" topology built, time = " << topo_time - intersect_time <<
"\n";
3375 bool pwn = is_pwn(tm_si, tm_si_topo);
3378 std::cout <<
" pwn checked, time = " << pwn_time - topo_time <<
"\n";
3382 if (dbg_level > 0) {
3383 std::cout <<
"Input is not PWN, using raycast method\n";
3385 if (hole_tolerant) {
3386 tm_out = raycast_tris_boolean(tm_si, op, nshapes, shape_fn, arena);
3389 PatchesInfo pinfo = find_patches(tm_si, tm_si_topo);
3390 tm_out = raycast_patches_boolean(tm_si, op, nshapes, shape_fn, pinfo, arena);
3394 std::cout <<
" raycast_boolean done, time = " << raycast_time - pwn_time <<
"\n";
3398 PatchesInfo pinfo = find_patches(tm_si, tm_si_topo);
3401 std::cout <<
" patches found, time = " << patch_time - pwn_time <<
"\n";
3403 CellsInfo cinfo = find_cells(tm_si, tm_si_topo, pinfo);
3404 if (dbg_level > 0) {
3405 std::cout <<
"Input is PWN\n";
3409 std::cout <<
" cells found, time = " << cell_time - pwn_time <<
"\n";
3411 finish_patch_cell_graph(tm_si, cinfo, pinfo, tm_si_topo, arena);
3414 std::cout <<
" finished patch-cell graph, time = " << finish_pc_time - cell_time <<
"\n";
3416 bool pc_ok = patch_cell_graph_ok(cinfo, pinfo);
3419 std::cout <<
"Something funny about input or a bug in boolean\n";
3420 return IMesh(tm_in);
3422 cinfo.init_windings(nshapes);
3423 int c_ambient = find_ambient_cell(tm_si,
nullptr, tm_si_topo, pinfo, arena);
3426 std::cout <<
" ambient cell found, time = " << amb_time - finish_pc_time <<
"\n";
3428 if (c_ambient == NO_INDEX) {
3430 std::cout <<
"Could not find an ambient cell; input not valid?\n";
3431 return IMesh(tm_si);
3433 propagate_windings_and_in_output_volume(pinfo, cinfo, c_ambient, op, nshapes, si_shape_fn);
3436 std::cout <<
" windings propagated, time = " << propagate_time - amb_time <<
"\n";
3438 tm_out = extract_from_in_output_volume_diffs(tm_si, pinfo, cinfo, arena);
3441 std::cout <<
" extracted, time = " << extract_time - propagate_time <<
"\n";
3443 if (dbg_level > 0) {
3445 TriMeshTopology tm_out_topo(tm_out);
3446 if (!is_pwn(tm_out, tm_out_topo)) {
3447 std::cout <<
"OUTPUT IS NOT PWN!\n";
3451 if (dbg_level > 1) {
3452 write_obj_mesh(tm_out,
"boolean_tm_output");
3453 std::cout <<
"boolean tm output:\n" << tm_out;
3457 std::cout <<
" boolean_trimesh done, total time = " << end_time - start_time <<
"\n";
3462 static void dump_test_spec(IMesh &imesh)
3464 std::cout <<
"test spec = " << imesh.vert_size() <<
" " << imesh.face_size() <<
"\n";
3465 for (
const Vert *
v : imesh.vertices()) {
3466 std::cout <<
v->co_exact[0] <<
" " <<
v->co_exact[1] <<
" " <<
v->co_exact[2] <<
" # "
3467 <<
v->
co[0] <<
" " <<
v->
co[1] <<
" " <<
v->
co[2] <<
"\n";
3469 for (
const Face *f : imesh.faces()) {
3470 for (
const Vert *fv : *f) {
3471 std::cout << imesh.lookup_vert(fv) <<
" ";
3481 IMesh boolean_mesh(IMesh &imesh,
3484 std::function<
int(
int)> shape_fn,
3487 IMesh *imesh_triangulated,
3490 constexpr
int dbg_level = 0;
3491 if (dbg_level > 0) {
3492 std::cout <<
"\nBOOLEAN_MESH\n"
3493 << nshapes <<
" operand" << (nshapes == 1 ?
"" :
"s")
3494 <<
" op=" << bool_optype_name(op) <<
"\n";
3495 if (dbg_level > 1) {
3496 write_obj_mesh(imesh,
"boolean_mesh_in");
3498 if (dbg_level > 2) {
3499 dump_test_spec(imesh);
3503 IMesh *tm_in = imesh_triangulated;
3504 IMesh our_triangulation;
3507 std::cout <<
"boolean_mesh, timing begins\n";
3509 if (tm_in ==
nullptr) {
3510 our_triangulation = triangulate_polymesh(imesh, arena);
3511 tm_in = &our_triangulation;
3515 std::cout <<
"triangulated, time = " << tri_time - start_time <<
"\n";
3517 if (dbg_level > 1) {
3518 write_obj_mesh(*tm_in,
"boolean_tm_in");
3520 IMesh tm_out = boolean_trimesh(*tm_in, op, nshapes, shape_fn, use_self, hole_tolerant, arena);
3523 std::cout <<
"boolean_trimesh done, time = " << bool_tri_time - tri_time <<
"\n";
3525 if (dbg_level > 1) {
3526 std::cout <<
"bool_trimesh_output:\n" << tm_out;
3527 write_obj_mesh(tm_out,
"bool_trimesh_output");
3529 IMesh ans = polymesh_from_trimesh_with_dissolve(tm_out, imesh, arena);
3532 std::cout <<
"polymesh from dissolving, time = " << dissolve_time - bool_tri_time <<
"\n";
3534 if (dbg_level > 0) {
3535 std::cout <<
"boolean_mesh output:\n" << ans;
3536 if (dbg_level > 2) {
3537 ans.populate_vert();
3538 dump_test_spec(ans);
3543 std::cout <<
"boolean_mesh done, total time = " << end_time - start_time <<
"\n";
typedef float(TangentPoint)[2]
void BLI_bvhtree_balance(BVHTree *tree)
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
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(BVHTree *tree, const float co[3], const float dir[3], float radius, float hit_dist, BVHTree_RayCastCallback callback, void *userdata)
Math vector functions needed specifically for mesh intersect and boolean.
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], const float epsilon)
const char * BLI_getenv(const char *env) ATTR_NONNULL(1)
#define UNUSED_VARS_NDEBUG(...)
typedef double(DMatrix)[4][4]
static uint8 component(Color32 c, uint i)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint i1
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble v1
Platform independent time functions.
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
DBVT_INLINE bool Intersect(const btDbvtAabbMm &a, const btDbvtAabbMm &b)
void sort(btMatrix3x3 &U, btVector3 &sigma, btMatrix3x3 &V, int t)
Helper function of 3X3 SVD for sorting singular values.
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.
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
bool closest(btVector3 &v)
IconTextureDrawCall normal
ThreadQueue * queue
all scheduled work for the cpu
std::ostream & operator<<(std::ostream &os, const CDT_result< T > &r)
constexpr bool operator==(StringRef a, StringRef b)
int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d)
uint64_t get_default_hash_2(const T1 &v1, const T2 &v2)
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
unsigned __int64 uint64_t
static double dot(const double3 &a, const double3 &b)
double PIL_check_seconds_timer(void)
__forceinline avxf cross(const avxf &a, const avxf &b)
__forceinline const avxi abs(const avxi &a)
ccl_device_inline float dot(const float2 &a, const float2 &b)
ccl_device_inline float2 fabs(const float2 &a)
ccl_device_inline float len_squared(const float3 a)