Blender  V2.93
mesh_boolean.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #ifdef WITH_GMP
22 
23 # include <algorithm>
24 # include <fstream>
25 # include <iostream>
26 
27 # include "BLI_array.hh"
28 # include "BLI_assert.h"
29 # include "BLI_delaunay_2d.h"
30 # include "BLI_double3.hh"
31 # include "BLI_float3.hh"
32 # include "BLI_hash.hh"
33 # include "BLI_kdopbvh.h"
34 # include "BLI_map.hh"
35 # include "BLI_math.h"
36 # include "BLI_math_boolean.hh"
37 # include "BLI_math_geom.h"
38 # include "BLI_math_mpq.hh"
39 # include "BLI_mesh_intersect.hh"
40 # include "BLI_mpq3.hh"
41 # include "BLI_set.hh"
42 # include "BLI_span.hh"
43 # include "BLI_stack.hh"
44 # include "BLI_vector.hh"
45 # include "BLI_vector_set.hh"
46 
47 # include "PIL_time.h"
48 
49 # include "BLI_mesh_boolean.hh"
50 
51 // # define PERFDEBUG
52 
53 namespace blender::meshintersect {
54 
60 class Edge {
61  const Vert *v_[2]{nullptr, nullptr};
62 
63  public:
64  Edge() = default;
65  Edge(const Vert *v0, const Vert *v1)
66  {
67  if (v0->id <= v1->id) {
68  v_[0] = v0;
69  v_[1] = v1;
70  }
71  else {
72  v_[0] = v1;
73  v_[1] = v0;
74  }
75  }
76 
77  const Vert *v0() const
78  {
79  return v_[0];
80  }
81 
82  const Vert *v1() const
83  {
84  return v_[1];
85  }
86 
87  const Vert *operator[](int i) const
88  {
89  return v_[i];
90  }
91 
92  bool operator==(Edge other) const
93  {
94  return v_[0]->id == other.v_[0]->id && v_[1]->id == other.v_[1]->id;
95  }
96 
97  uint64_t hash() const
98  {
99  return get_default_hash_2(v_[0]->id, v_[1]->id);
100  }
101 };
102 
103 static std::ostream &operator<<(std::ostream &os, const Edge &e)
104 {
105  if (e.v0() == nullptr) {
106  BLI_assert(e.v1() == nullptr);
107  os << "(null,null)";
108  }
109  else {
110  os << "(" << e.v0() << "," << e.v1() << ")";
111  }
112  return os;
113 }
114 
115 static std::ostream &operator<<(std::ostream &os, const Span<int> &a)
116 {
117  for (int i : a.index_range()) {
118  os << a[i];
119  if (i != a.size() - 1) {
120  os << " ";
121  }
122  }
123  return os;
124 }
125 
126 static std::ostream &operator<<(std::ostream &os, const Array<int> &iarr)
127 {
128  os << Span<int>(iarr);
129  return os;
130 }
131 
133 class TriMeshTopology : NonCopyable {
135  Map<Edge, Vector<int> *> edge_tri_;
137  Map<const Vert *, Vector<Edge>> vert_edges_;
138 
139  public:
140  TriMeshTopology(const IMesh &tm);
141  ~TriMeshTopology();
142 
143  /* If e is manifold, return index of the other triangle (not t) that has it.
144  * Else return NO_INDEX. */
145  int other_tri_if_manifold(Edge e, int t) const
146  {
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];
151  }
152  }
153  return NO_INDEX;
154  }
155 
156  /* Which triangles share edge e (in either orientation)? */
157  const Vector<int> *edge_tris(Edge e) const
158  {
159  return edge_tri_.lookup_default(e, nullptr);
160  }
161 
162  /* Which edges are incident on the given vertex?
163  * We assume v has some incident edges. */
164  const Vector<Edge> &vert_edges(const Vert *v) const
165  {
166  return vert_edges_.lookup(v);
167  }
168 
169  Map<Edge, Vector<int> *>::ItemIterator edge_tri_map_items() const
170  {
171  return edge_tri_.items();
172  }
173 };
174 
175 TriMeshTopology::TriMeshTopology(const IMesh &tm)
176 {
177  const int dbg_level = 0;
178  if (dbg_level > 0) {
179  std::cout << "TRIMESHTOPOLOGY CONSTRUCTION\n";
180  }
181  /* If everything were manifold, `F+V-E=2` and `E=3F/2`.
182  * So an likely overestimate, allowing for non-manifoldness, is `E=2F` and `V=F`. */
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);
189  BLI_assert(tri.is_tri());
190  for (int i = 0; i < 3; ++i) {
191  const Vert *v = tri[i];
192  const Vert *vnext = tri[(i + 1) % 3];
193  Edge e(v, vnext);
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);
198  BLI_assert(edges != nullptr);
199  }
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);
204  }
205  }
206  /* Debugging. */
207  if (dbg_level > 0) {
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;
212  if (print_stats) {
213  edge_tri_.print_stats();
214  }
215  }
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";
220  }
221  std::cout << "\n";
222  }
223  }
224 }
225 
226 TriMeshTopology::~TriMeshTopology()
227 {
228  for (const Vector<int> *vec : edge_tri_.values()) {
229  delete vec;
230  }
231 }
232 
234 class Patch {
235  Vector<int> tri_; /* Indices of triangles in the Patch. */
236 
237  public:
238  Patch() = default;
239 
240  void add_tri(int t)
241  {
242  tri_.append(t);
243  }
244 
245  int tot_tri() const
246  {
247  return tri_.size();
248  }
249 
250  int tri(int i) const
251  {
252  return tri_[i];
253  }
254 
255  IndexRange tri_range() const
256  {
257  return IndexRange(tri_.size());
258  }
259 
260  Span<int> tris() const
261  {
262  return Span<int>(tri_);
263  }
264 
265  int cell_above{NO_INDEX};
266  int cell_below{NO_INDEX};
267  int component{NO_INDEX};
268 };
269 
270 static std::ostream &operator<<(std::ostream &os, const Patch &patch)
271 {
272  os << "Patch " << patch.tris();
273  if (patch.cell_above != NO_INDEX) {
274  os << " cell_above=" << patch.cell_above;
275  }
276  else {
277  os << " cell_above not set";
278  }
279  if (patch.cell_below != NO_INDEX) {
280  os << " cell_below=" << patch.cell_below;
281  }
282  else {
283  os << " cell_below not set";
284  }
285  return os;
286 }
287 
288 class PatchesInfo {
290  Vector<Patch> patch_;
292  Array<int> tri_patch_;
294  Map<std::pair<int, int>, Edge> pp_edge_;
295 
296  public:
297  explicit PatchesInfo(int ntri)
298  {
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);
302  }
303 
304  int tri_patch(int t) const
305  {
306  return tri_patch_[t];
307  }
308 
309  int add_patch()
310  {
311  int patch_index = patch_.append_and_get_index(Patch());
312  return patch_index;
313  }
314 
315  void grow_patch(int patch_index, int t)
316  {
317  tri_patch_[t] = patch_index;
318  patch_[patch_index].add_tri(t);
319  }
320 
321  bool tri_is_assigned(int t) const
322  {
323  return tri_patch_[t] != NO_INDEX;
324  }
325 
326  const Patch &patch(int patch_index) const
327  {
328  return patch_[patch_index];
329  }
330 
331  Patch &patch(int patch_index)
332  {
333  return patch_[patch_index];
334  }
335 
336  int tot_patch() const
337  {
338  return patch_.size();
339  }
340 
341  IndexRange index_range() const
342  {
343  return IndexRange(patch_.size());
344  }
345 
346  const Patch *begin() const
347  {
348  return patch_.begin();
349  }
350 
351  const Patch *end() const
352  {
353  return patch_.end();
354  }
355 
356  Patch *begin()
357  {
358  return patch_.begin();
359  }
360 
361  Patch *end()
362  {
363  return patch_.end();
364  }
365 
366  void add_new_patch_patch_edge(int p1, int p2, Edge e)
367  {
368  pp_edge_.add_new(std::pair<int, int>(p1, p2), e);
369  pp_edge_.add_new(std::pair<int, int>(p2, p1), e);
370  }
371 
372  Edge patch_patch_edge(int p1, int p2)
373  {
374  return pp_edge_.lookup_default(std::pair<int, int>(p1, p2), Edge());
375  }
376 
377  const Map<std::pair<int, int>, Edge> &patch_patch_edge_map()
378  {
379  return pp_edge_;
380  }
381 };
382 
383 static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding);
384 
390 class Cell {
391  Set<int> patches_;
392  Array<int> winding_;
393  int merged_to_{NO_INDEX};
394  bool winding_assigned_{false};
395  /* in_output_volume_ will be true when this cell should be in the output volume. */
396  bool in_output_volume_{false};
397  /* zero_volume_ will be true when this is a zero-volume cell (inside a stack of identical
398  * triangles). */
399  bool zero_volume_{false};
400 
401  public:
402  Cell() = default;
403 
404  void add_patch(int p)
405  {
406  patches_.add(p);
407  zero_volume_ = false; /* If it was true before, it no longer is. */
408  }
409 
410  const Set<int> &patches() const
411  {
412  return patches_;
413  }
414 
416  int patch_other(int p) const
417  {
418  if (patches_.size() != 2) {
419  return NO_INDEX;
420  }
421  for (int pother : patches_) {
422  if (pother != p) {
423  return pother;
424  }
425  }
426  return NO_INDEX;
427  }
428 
429  Span<int> winding() const
430  {
431  return Span<int>(winding_);
432  }
433 
434  void init_winding(int winding_len)
435  {
436  winding_ = Array<int>(winding_len);
437  }
438 
439  void seed_ambient_winding()
440  {
441  winding_.fill(0);
442  winding_assigned_ = true;
443  }
444 
445  void set_winding_and_in_output_volume(const Cell &from_cell,
446  int shape,
447  int delta,
448  BoolOpType bool_optype)
449  {
450  std::copy(from_cell.winding().begin(), from_cell.winding().end(), winding_.begin());
451  if (shape >= 0) {
452  winding_[shape] += delta;
453  }
454  winding_assigned_ = true;
455  in_output_volume_ = apply_bool_op(bool_optype, winding_);
456  }
457 
458  bool in_output_volume() const
459  {
460  return in_output_volume_;
461  }
462 
463  bool winding_assigned() const
464  {
465  return winding_assigned_;
466  }
467 
468  bool zero_volume() const
469  {
470  return zero_volume_;
471  }
472 
473  int merged_to() const
474  {
475  return merged_to_;
476  }
477 
478  void set_merged_to(int c)
479  {
480  merged_to_ = c;
481  }
482 
487  void check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh);
488 };
489 
490 static std::ostream &operator<<(std::ostream &os, const Cell &cell)
491 {
492  os << "Cell patches";
493  for (int p : cell.patches()) {
494  std::cout << " " << p;
495  }
496  if (cell.winding().size() > 0) {
497  os << " winding=" << cell.winding();
498  os << " in_output_volume=" << cell.in_output_volume();
499  }
500  os << " zv=" << cell.zero_volume();
501  std::cout << "\n";
502  return os;
503 }
504 
505 static bool tris_have_same_verts(const IMesh &mesh, int t1, int t2)
506 {
507  const Face &tri1 = *mesh.face(t1);
508  const Face &tri2 = *mesh.face(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]));
513  }
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]));
517  }
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]));
521  }
522  return false;
523 }
524 
530 void Cell::check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh)
531 {
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) {
537  p1_index = p;
538  }
539  else {
540  p2_index = p;
541  }
542  }
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))) {
548  zero_volume_ = true;
549  }
550  }
551  }
552 }
553 
554 /* Information about all the Cells. */
555 class CellsInfo {
556  Vector<Cell> cell_;
557 
558  public:
559  CellsInfo() = default;
560 
561  int add_cell()
562  {
563  int index = cell_.append_and_get_index(Cell());
564  return index;
565  }
566 
567  Cell &cell(int c)
568  {
569  return cell_[c];
570  }
571 
572  const Cell &cell(int c) const
573  {
574  return cell_[c];
575  }
576 
577  int tot_cell() const
578  {
579  return cell_.size();
580  }
581 
582  IndexRange index_range() const
583  {
584  return cell_.index_range();
585  }
586 
587  const Cell *begin() const
588  {
589  return cell_.begin();
590  }
591 
592  const Cell *end() const
593  {
594  return cell_.end();
595  }
596 
597  Cell *begin()
598  {
599  return cell_.begin();
600  }
601 
602  Cell *end()
603  {
604  return cell_.end();
605  }
606 
607  void init_windings(int winding_len)
608  {
609  for (Cell &cell : cell_) {
610  cell.init_winding(winding_len);
611  }
612  }
613 };
614 
618 static void write_obj_cell_patch(const IMesh &m,
619  const CellsInfo &cinfo,
620  const PatchesInfo &pinfo,
621  bool cells_only,
622  const std::string &name)
623 {
624  /* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library.
625  * This is just for developer debugging anyway,
626  * and should never be called in production Blender. */
627 # ifdef _WIN_32
628  const char *objdir = BLI_getenv("HOME");
629 # else
630  const char *objdir = "/tmp/";
631 # endif
632 
633  std::string fname = std::string(objdir) + name + std::string("_cellpatch.obj");
634  std::ofstream f;
635  f.open(fname);
636  if (!f) {
637  std::cout << "Could not open file " << fname << "\n";
638  return;
639  }
640 
641  /* Copy IMesh so can populate verts. */
642  IMesh mm = m;
643  mm.populate_vert();
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";
648  }
649  if (!cells_only) {
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);
655  f << "f ";
656  for (const Vert *v : tri) {
657  f << mm.lookup_vert(v) + 1 << " ";
658  }
659  f << "\n";
660  }
661  }
662  }
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);
670  f << "f ";
671  for (const Vert *v : tri) {
672  f << mm.lookup_vert(v) + 1 << " ";
673  }
674  f << "\n";
675  }
676  }
677  }
678  f.close();
679 }
680 
681 static void merge_cells(int merge_to, int merge_from, CellsInfo &cinfo, PatchesInfo &pinfo)
682 {
683  if (merge_to == merge_from) {
684  return;
685  }
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);
692  }
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;
698  }
699  if (patch.cell_below == merge_from) {
700  patch.cell_below = merge_to;
701  }
702  }
703  merge_from_cell.set_merged_to(final_merge_to);
704 }
705 
709 static PatchesInfo find_patches(const IMesh &tm, const TriMeshTopology &tmtopo)
710 {
711  const int dbg_level = 0;
712  if (dbg_level > 0) {
713  std::cout << "\nFIND_PATCHES\n";
714  }
715  int ntri = tm.face_size();
716  PatchesInfo pinfo(ntri);
717  /* Algorithm: Grow patches across manifold edges as long as there are unassigned triangles. */
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();
725  if (dbg_level > 1) {
726  std::cout << "pop tcand = " << tcand << "; assigned = " << pinfo.tri_is_assigned(tcand)
727  << "\n";
728  }
729  if (pinfo.tri_is_assigned(tcand)) {
730  continue;
731  }
732  if (dbg_level > 1) {
733  std::cout << "grow patch from seed tcand=" << tcand << "\n";
734  }
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);
740  if (dbg_level > 1) {
741  std::cout << " edge " << e << " generates t_other=" << t_other << "\n";
742  }
743  if (t_other != NO_INDEX) {
744  if (!pinfo.tri_is_assigned(t_other)) {
745  if (dbg_level > 1) {
746  std::cout << " push t_other = " << t_other << "\n";
747  }
748  cur_patch_grow.push(t_other);
749  }
750  }
751  else {
752  /* e is non-manifold. Set any patch-patch incidences we can. */
753  if (dbg_level > 1) {
754  std::cout << " e non-manifold case\n";
755  }
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) {
763  continue;
764  }
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);
767  if (dbg_level > 1) {
768  std::cout << "added patch_patch_edge (" << cur_patch_index << "," << p_other
769  << ") = " << e << "\n";
770  }
771  }
772  }
773  }
774  }
775  }
776  }
777  }
778  }
779  }
780  if (dbg_level > 0) {
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";
784  }
785  if (dbg_level > 1) {
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";
789  }
790  }
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";
797  }
798  }
799  }
800  }
801  return pinfo;
802 }
803 
810 static const Vert *find_flap_vert(const Face &tri, const Edge e, bool *r_rev)
811 {
812  *r_rev = false;
813  const Vert *flapv;
814  if (tri[0] == e.v0()) {
815  if (tri[1] == e.v1()) {
816  *r_rev = false;
817  flapv = tri[2];
818  }
819  else {
820  if (tri[2] != e.v1()) {
821  return nullptr;
822  }
823  *r_rev = true;
824  flapv = tri[1];
825  }
826  }
827  else if (tri[1] == e.v0()) {
828  if (tri[2] == e.v1()) {
829  *r_rev = false;
830  flapv = tri[0];
831  }
832  else {
833  if (tri[0] != e.v1()) {
834  return nullptr;
835  }
836  *r_rev = true;
837  flapv = tri[2];
838  }
839  }
840  else {
841  if (tri[2] != e.v0()) {
842  return nullptr;
843  }
844  if (tri[0] == e.v1()) {
845  *r_rev = false;
846  flapv = tri[1];
847  }
848  else {
849  if (tri[1] != e.v1()) {
850  return nullptr;
851  }
852  *r_rev = true;
853  flapv = tri[0];
854  }
855  }
856  return flapv;
857 }
858 
873 static int sort_tris_class(const Face &tri, const Face &tri0, const Edge e)
874 {
875  const int dbg_level = 0;
876  if (dbg_level > 0) {
877  std::cout << "classify e = " << e << "\n";
878  }
879  mpq3 a0 = tri0[0]->co_exact;
880  mpq3 a1 = tri0[1]->co_exact;
881  mpq3 a2 = tri0[2]->co_exact;
882  bool rev;
883  bool rev0;
884  const Vert *flapv0 = find_flap_vert(tri0, e, &rev0);
885  const Vert *flapv = find_flap_vert(tri, e, &rev);
886  if (dbg_level > 0) {
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";
891  }
892  BLI_assert(flapv != nullptr && flapv0 != nullptr);
893  const mpq3 flap = flapv->co_exact;
894  /* orient will be positive if flap is below oriented plane of a0,a1,a2. */
895  int orient = orient3d(a0, a1, a2, flap);
896  int ans;
897  if (orient > 0) {
898  ans = rev0 ? 4 : 3;
899  }
900  else if (orient < 0) {
901  ans = rev0 ? 3 : 4;
902  }
903  else {
904  ans = flapv == flapv0 ? 1 : 2;
905  }
906  if (dbg_level > 0) {
907  std::cout << " orient = " << orient << " ans = " << ans << "\n";
908  }
909  return ans;
910 }
911 
912 constexpr int EXTRA_TRI_INDEX = INT_MAX;
913 
920 static void sort_by_signed_triangle_index(Vector<int> &g,
921  const Edge e,
922  const IMesh &tm,
923  const Face *extra_tri)
924 {
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]);
928  bool rev;
929  find_flap_vert(tri, e, &rev);
930  signed_g[i] = rev ? -g[i] : g[i];
931  }
932  std::sort(signed_g.begin(), signed_g.end());
933 
934  for (int i : g.index_range()) {
935  g[i] = abs(signed_g[i]);
936  }
937 }
938 
953 static Array<int> sort_tris_around_edge(const IMesh &tm,
954  const TriMeshTopology &tmtopo,
955  const Edge e,
956  const Span<int> tris,
957  const int t0,
958  const Face *extra_tri)
959 {
960  /* Divide and conquer, quick-sort-like sort.
961  * Pick a triangle t0, then partition into groups:
962  * (1) co-planar with t0 and on same side of e
963  * (2) co-planar with t0 and on opposite side of e
964  * (3) below plane of t0
965  * (4) above plane of t0
966  * Each group is sorted and then the sorts are merged to give the answer.
967  * We don't expect the input array to be very large - should typically
968  * be only 3 or 4 - so OK to make copies of arrays instead of swapping
969  * around in a single array. */
970  const int dbg_level = 0;
971  if (tris.size() == 0) {
972  return Array<int>();
973  }
974  if (dbg_level > 0) {
975  if (t0 == tris[0]) {
976  std::cout << "\n";
977  }
978  std::cout << "sort_tris_around_edge " << e << "\n";
979  std::cout << "tris = " << tris << "\n";
980  std::cout << "t0 = " << t0 << "\n";
981  }
982  Vector<int> g1{tris[0]};
983  Vector<int> g2;
984  Vector<int> g3;
985  Vector<int> g4;
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()) {
989  if (i == 0) {
990  continue;
991  }
992  int t = tris[i];
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);
995  if (dbg_level > 2) {
996  std::cout << "classifying tri " << t << " with respect to " << tris[0] << "\n";
997  }
998  int group_num = sort_tris_class(tri, triref, e);
999  if (dbg_level > 2) {
1000  std::cout << " classify result : " << group_num << "\n";
1001  }
1002  groups[group_num - 1]->append(t);
1003  }
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";
1009  }
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";
1014  }
1015  }
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";
1020  }
1021  }
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";
1027  }
1028  }
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";
1034  }
1035  }
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) {
1040  p = std::copy(g1.begin(), g1.end(), p);
1041  p = std::copy(g4.begin(), g4.end(), p);
1042  p = std::copy(g2.begin(), g2.end(), p);
1043  std::copy(g3.begin(), g3.end(), p);
1044  }
1045  else {
1046  p = std::copy(g3.begin(), g3.end(), p);
1047  p = std::copy(g1.begin(), g1.end(), p);
1048  p = std::copy(g4.begin(), g4.end(), p);
1049  std::copy(g2.begin(), g2.end(), p);
1050  }
1051  if (dbg_level > 0) {
1052  std::cout << "sorted tris = " << ans << "\n";
1053  }
1054  return ans;
1055 }
1056 
1063 static void find_cells_from_edge(const IMesh &tm,
1064  const TriMeshTopology &tmtopo,
1065  PatchesInfo &pinfo,
1066  CellsInfo &cinfo,
1067  const Edge e)
1068 {
1069  const int dbg_level = 0;
1070  if (dbg_level > 0) {
1071  std::cout << "FIND_CELLS_FROM_EDGE " << e << "\n";
1072  }
1073  const Vector<int> *edge_tris = tmtopo.edge_tris(e);
1074  BLI_assert(edge_tris != nullptr);
1075  Array<int> sorted_tris = sort_tris_around_edge(
1076  tm, tmtopo, e, Span<int>(*edge_tris), (*edge_tris)[0], nullptr);
1077 
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";
1084  }
1085  }
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);
1092  bool r_flipped;
1093  bool rnext_flipped;
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";
1105  }
1106  if (*r_follow_cell == NO_INDEX && *rnext_prev_cell == NO_INDEX) {
1107  /* Neither is assigned: make a new cell. */
1108  int c = cinfo.add_cell();
1109  *r_follow_cell = c;
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"
1118  << c << "\n";
1119  std::cout << " p" << rnext_index << "." << (rnext_flipped ? "cell_above" : "cell_below")
1120  << " = c" << c << "\n";
1121  }
1122  }
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";
1132  }
1133  }
1134  else if (*r_follow_cell == NO_INDEX && *rnext_prev_cell != NO_INDEX) {
1135  int c = *rnext_prev_cell;
1136  *r_follow_cell = c;
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";
1143  }
1144  }
1145  else {
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
1152  << "\n";
1153  }
1154  merge_cells(*r_follow_cell, *rnext_prev_cell, cinfo, pinfo);
1155  }
1156  }
1157  else {
1158  if (dbg_level > 0) {
1159  std::cout << " merge cell " << *r_follow_cell << " into cell " << *rnext_prev_cell
1160  << "\n";
1161  }
1162  merge_cells(*rnext_prev_cell, *r_follow_cell, cinfo, pinfo);
1163  }
1164  }
1165  }
1166 }
1167 
1172 static CellsInfo find_cells(const IMesh &tm, const TriMeshTopology &tmtopo, PatchesInfo &pinfo)
1173 {
1174  const int dbg_level = 0;
1175  if (dbg_level > 0) {
1176  std::cout << "\nFIND_CELLS\n";
1177  }
1178  CellsInfo cinfo;
1179  /* For each unique edge shared between patch pairs, process it. */
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;
1184  if (p < q) {
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);
1189  }
1190  }
1191  }
1192  /* Some patches may have no cells at this point. These are either:
1193  * (a) a closed manifold patch only incident on itself (sphere, torus, klein bottle, etc.).
1194  * (b) an open manifold patch only incident on itself (has non-manifold boundaries).
1195  * Make above and below cells for these patches. This will create a disconnected patch-cell
1196  * bipartite graph, which will have to be fixed later. */
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);
1203  cell.add_patch(p);
1204  }
1205  if (patch.cell_below == NO_INDEX) {
1206  int c = cinfo.add_cell();
1207  patch.cell_below = c;
1208  Cell &cell = cinfo.cell(c);
1209  cell.add_patch(p);
1210  }
1211  }
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";
1216  }
1217  std::cout << "Patches\n";
1218  for (int i : pinfo.index_range()) {
1219  std::cout << i << ": " << pinfo.patch(i) << "\n";
1220  }
1221  if (dbg_level > 1) {
1222  write_obj_cell_patch(tm, cinfo, pinfo, false, "postfindcells");
1223  }
1224  }
1225  return cinfo;
1226 }
1227 
1233 static Vector<Vector<int>> find_patch_components(const CellsInfo &cinfo, PatchesInfo &pinfo)
1234 {
1235  constexpr int dbg_level = 0;
1236  if (dbg_level > 0) {
1237  std::cout << "FIND_PATCH_COMPONENTS\n";
1238  }
1239  if (pinfo.tot_patch() == 0) {
1240  return Vector<Vector<int>>();
1241  }
1242  int current_component = 0;
1243  Array<bool> cell_processed(cinfo.tot_cell(), false);
1244  Stack<int> stack; /* Patch indices to visit. */
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) {
1249  continue;
1250  }
1251  ans.append(Vector<int>());
1252  ans[current_component].append(pstart);
1253  stack.push(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]) {
1261  continue;
1262  }
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;
1268  stack.push(pn);
1269  ans[current_component].append(pn);
1270  }
1271  }
1272  }
1273  }
1274  ++current_component;
1275  }
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";
1280  }
1281  }
1282  return ans;
1283 }
1284 
1289 static bool patch_cell_graph_ok(const CellsInfo &cinfo, const PatchesInfo &pinfo)
1290 {
1291  for (int c : cinfo.index_range()) {
1292  const Cell &cell = cinfo.cell(c);
1293  if (cell.merged_to() != NO_INDEX) {
1294  continue;
1295  }
1296  if (cell.patches().size() == 0) {
1297  std::cout << "Patch/Cell graph disconnected at Cell " << c << " with no patches\n";
1298  return false;
1299  }
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";
1303  return false;
1304  }
1305  }
1306  }
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";
1312  return false;
1313  }
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";
1316  return false;
1317  }
1318  }
1319  return true;
1320 }
1321 
1335 static bool is_pwn(const IMesh &tm, const TriMeshTopology &tmtopo)
1336 {
1337  constexpr int dbg_level = 0;
1338  for (auto item : tmtopo.edge_tri_map_items()) {
1339  const Edge &edge = item.key;
1340  int tot_orient = 0;
1341  /* For each face t attached to edge, add +1 if the edge
1342  * is positively in t, and -1 if negatively in t. */
1343  for (int t : *item.value) {
1344  const Face &face = *tm.face(t);
1345  BLI_assert(face.size() == 3);
1346  for (int i : face.index_range()) {
1347  if (face[i] == edge.v0()) {
1348  if (face[(i + 1) % 3] == edge.v1()) {
1349  ++tot_orient;
1350  }
1351  else {
1352  BLI_assert(face[(i + 3 - 1) % 3] == edge.v1());
1353  --tot_orient;
1354  }
1355  }
1356  }
1357  }
1358  if (tot_orient != 0) {
1359  if (dbg_level > 0) {
1360  std::cout << "edge causing non-pwn: " << edge << "\n";
1361  }
1362  return false;
1363  }
1364  }
1365  return true;
1366 }
1367 
1375 static int find_cell_for_point_near_edge(mpq3 p,
1376  const Edge &e,
1377  const IMesh &tm,
1378  const TriMeshTopology &tmtopo,
1379  const PatchesInfo &pinfo,
1380  IMeshArena *arena)
1381 {
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";
1385  }
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},
1389  NO_INDEX,
1390  {NO_INDEX, NO_INDEX, NO_INDEX},
1391  {false, false, false});
1392  BLI_assert(etris != nullptr);
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";
1400  }
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
1410  << "\n";
1411  }
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";
1415  }
1416  bool prev_flipped;
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";
1421  }
1422  return c;
1423 }
1424 
1439 static int find_ambient_cell(const IMesh &tm,
1440  const Vector<int> *component_patches,
1441  const TriMeshTopology &tmtopo,
1442  const PatchesInfo &pinfo,
1443  IMeshArena *arena)
1444 {
1445  int dbg_level = 0;
1446  if (dbg_level > 0) {
1447  std::cout << "FIND_AMBIENT_CELL\n";
1448  }
1449  /* First find a vertex with the maximum x value. */
1450  /* Prefer not to populate the verts in the #IMesh just for this. */
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) {
1460  v_extreme = v;
1461  extreme_x = x;
1462  }
1463  }
1464  }
1465  }
1466  else {
1467  if (dbg_level > 0) {
1468  std::cout << "restrict to patches " << *component_patches << "\n";
1469  }
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) {
1479  v_extreme = v;
1480  extreme_x = x;
1481  }
1482  }
1483  }
1484  }
1485  }
1486  if (dbg_level > 0) {
1487  std::cout << "v_extreme = " << v_extreme << "\n";
1488  }
1489  /* Find edge attached to v_extreme with max absolute slope
1490  * when projected onto the XY plane. That edge is guaranteed to
1491  * be on the convex hull of the mesh. */
1492  const Vector<Edge> &edges = tmtopo.vert_edges(v_extreme);
1493  const mpq_class extreme_y = v_extreme->co_exact.y;
1494  Edge ehull;
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;
1500  if (delta_x == 0) {
1501  /* Vertical slope. */
1502  ehull = e;
1503  break;
1504  }
1505  mpq_class abs_slope = abs((co_other.y - extreme_y) / delta_x);
1506  if (abs_slope > max_abs_slope) {
1507  ehull = e;
1508  max_abs_slope = abs_slope;
1509  }
1510  }
1511  if (dbg_level > 0) {
1512  std::cout << "ehull = " << ehull << " slope = " << max_abs_slope << "\n";
1513  }
1514  /* Sort triangles around ehull, including a dummy triangle that include a known point in ambient
1515  * cell. */
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";
1521  }
1522  return c_ambient;
1523 }
1524 
1534 static Edge find_good_sorting_edge(const Vert *testp,
1535  const Vert *closestp,
1536  const TriMeshTopology &tmtopo)
1537 {
1538  constexpr int dbg_level = 0;
1539  if (dbg_level > 0) {
1540  std::cout << "FIND_GOOD_SORTING_EDGE testp = " << testp << ", closestp = " << closestp << "\n";
1541  }
1542  /* We want to project the edges incident to closestp onto a plane
1543  * whose ordinate direction will be regarded as going from closestp to testp,
1544  * and whose abscissa direction is some perpendicular to that.
1545  * A perpendicular direction can be found by swapping two coordinates
1546  * and negating one, and zeroing out the third, being careful that one
1547  * of the swapped vertices is non-zero. */
1548  const mpq3 &co_closest = closestp->co_exact;
1549  const mpq3 &co_test = testp->co_exact;
1550  BLI_assert(co_test != co_closest);
1551  mpq3 abscissa = co_test - co_closest;
1552  /* Find a non-zero-component axis of abscissa. */
1553  int axis;
1554  for (axis = 0; axis < 3; ++axis) {
1555  if (abscissa[axis] != 0) {
1556  break;
1557  }
1558  }
1559  BLI_assert(axis < 3);
1560  int axis_next = (axis + 1) % 3;
1561  int axis_next_next = (axis_next + 1) % 3;
1562  mpq3 ordinate;
1563  ordinate[axis] = abscissa[axis_next];
1564  ordinate[axis_next] = -abscissa[axis];
1565  ordinate[axis_next_next] = 0;
1566  /* By construction, dot(abscissa, ordinate) == 0, so they are perpendicular. */
1567  mpq3 normal = mpq3::cross(abscissa, ordinate);
1568  if (dbg_level > 0) {
1569  std::cout << "abscissa = " << abscissa << "\n";
1570  std::cout << "ordinate = " << ordinate << "\n";
1571  std::cout << "normal = " << normal << "\n";
1572  }
1573  mpq_class nlen2 = normal.length_squared();
1574  mpq_class max_abs_slope = -1;
1575  Edge esort;
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;
1581  /* Get projection of evec onto plane of abscissa and ordinate. */
1582  mpq3 proj_evec = evec - (mpq3::dot(evec, normal) / nlen2) * normal;
1583  /* The projection calculations along the abscissa and ordinate should
1584  * be scaled by 1/abscissa and 1/ordinate respectively,
1585  * but we can skip: it won't affect which `evec` has the maximum slope. */
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";
1593  }
1594  if (evec_a == 0) {
1595  /* evec is perpendicular to abscissa. */
1596  esort = e;
1597  if (dbg_level > 0) {
1598  std::cout << "perpendicular esort is " << esort << "\n";
1599  }
1600  break;
1601  }
1602  mpq_class abs_slope = abs(evec_o / evec_a);
1603  if (abs_slope > max_abs_slope) {
1604  esort = e;
1605  max_abs_slope = abs_slope;
1606  if (dbg_level > 0) {
1607  std::cout << "with abs_slope " << abs_slope << " new esort is " << esort << "\n";
1608  }
1609  }
1610  }
1611  return esort;
1612 }
1613 
1626 static int find_containing_cell(const Vert *v,
1627  int t,
1628  int close_edge,
1629  int close_vert,
1630  const PatchesInfo &pinfo,
1631  const IMesh &tm,
1632  const TriMeshTopology &tmtopo,
1633  IMeshArena *arena)
1634 {
1635  constexpr int dbg_level = 0;
1636  if (dbg_level > 0) {
1637  std::cout << "FIND_CONTAINING_CELL v=" << v << ", t=" << t << "\n";
1638  }
1639  const Face &tri = *tm.face(t);
1640  Edge etest;
1641  if (close_edge == -1 && close_vert == -1) {
1642  /* Choose any edge if closest point is inside the triangle. */
1643  close_edge = 0;
1644  }
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 << " ";
1654  }
1655  std::cout << "\n";
1656  }
1657  for (Edge e : edges) {
1658  if ((e.v0() == v0 && e.v1() == v1) || (e.v0() == v1 && e.v1() == v0)) {
1659  etest = e;
1660  break;
1661  }
1662  }
1663  }
1664  else {
1665  int cv = close_vert;
1666  const Vert *vert_cv = tri[cv];
1667  if (vert_cv == v) {
1668  /* Need to use another one to find sorting edge. */
1669  vert_cv = tri[(cv + 1) % 3];
1670  BLI_assert(vert_cv != v);
1671  }
1672  etest = find_good_sorting_edge(v, vert_cv, tmtopo);
1673  }
1674  BLI_assert(etest.v0() != nullptr);
1675  if (dbg_level > 0) {
1676  std::cout << "etest = " << etest << "\n";
1677  }
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";
1681  }
1682  return c;
1683 }
1684 
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)
1696 {
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";
1701  }
1702  /* Check if p in vertex region outside a. */
1703  mpq3 ab = b - a;
1704  mpq3 ac = c - a;
1705  mpq3 ap = p - a;
1706  mpq_class d1 = mpq3::dot(ab, ap);
1707  mpq_class d2 = mpq3::dot(ac, ap);
1708  if (d1 <= 0 && d2 <= 0) {
1709  /* Barycentric coordinates (1,0,0). */
1710  *r_edge = -1;
1711  *r_vert = 0;
1712  if (dbg_level > 0) {
1713  std::cout << " answer = a\n";
1714  }
1715  return mpq3::distance_squared(p, a);
1716  }
1717  /* Check if p in vertex region outside b. */
1718  mpq3 bp = p - b;
1719  mpq_class d3 = mpq3::dot(ab, bp);
1720  mpq_class d4 = mpq3::dot(ac, bp);
1721  if (d3 >= 0 && d4 <= d3) {
1722  /* Barycentric coordinates (0,1,0). */
1723  *r_edge = -1;
1724  *r_vert = 1;
1725  if (dbg_level > 0) {
1726  std::cout << " answer = b\n";
1727  }
1728  return mpq3::distance_squared(p, b);
1729  }
1730  /* Check if p in region of ab. */
1731  mpq_class vc = d1 * d4 - d3 * d2;
1732  if (vc <= 0 && d1 >= 0 && d3 <= 0) {
1733  mpq_class v = d1 / (d1 - d3);
1734  /* Barycentric coordinates (1-v,v,0). */
1735  mpq3 r = a + v * ab;
1736  *r_vert = -1;
1737  *r_edge = 0;
1738  if (dbg_level > 0) {
1739  std::cout << " answer = on ab at " << r << "\n";
1740  }
1741  return mpq3::distance_squared(p, r);
1742  }
1743  /* Check if p in vertex region outside c. */
1744  mpq3 cp = p - c;
1745  mpq_class d5 = mpq3::dot(ab, cp);
1746  mpq_class d6 = mpq3::dot(ac, cp);
1747  if (d6 >= 0 && d5 <= d6) {
1748  /* Barycentric coordinates (0,0,1). */
1749  *r_edge = -1;
1750  *r_vert = 2;
1751  if (dbg_level > 0) {
1752  std::cout << " answer = c\n";
1753  }
1754  return mpq3::distance_squared(p, c);
1755  }
1756  /* Check if p in edge region of ac. */
1757  mpq_class vb = d5 * d2 - d1 * d6;
1758  if (vb <= 0 && d2 >= 0 && d6 <= 0) {
1759  mpq_class w = d2 / (d2 - d6);
1760  /* Barycentric coordinates (1-w,0,w). */
1761  mpq3 r = a + w * ac;
1762  *r_vert = -1;
1763  *r_edge = 2;
1764  if (dbg_level > 0) {
1765  std::cout << " answer = on ac at " << r << "\n";
1766  }
1767  return mpq3::distance_squared(p, r);
1768  }
1769  /* Check if p in edge region of bc. */
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));
1773  /* Barycentric coordinates (0,1-w,w). */
1774  mpq3 r = c - b;
1775  r = w * r;
1776  r = r + b;
1777  *r_vert = -1;
1778  *r_edge = 1;
1779  if (dbg_level > 0) {
1780  std::cout << " answer = on bc at " << r << "\n";
1781  }
1782  return mpq3::distance_squared(p, r);
1783  }
1784  /* p inside face region. Compute barycentric coordinates (u,v,w). */
1785  mpq_class denom = 1 / (va + vb + vc);
1786  mpq_class v = vb * denom;
1787  mpq_class w = vc * denom;
1788  ac = w * ac;
1789  mpq3 r = a + v * ab;
1790  r = r + ac;
1791  *r_vert = -1;
1792  *r_edge = -1;
1793  if (dbg_level > 0) {
1794  std::cout << " answer = inside at " << r << "\n";
1795  }
1796  return mpq3::distance_squared(p, r);
1797 }
1798 
1799 struct ComponentContainer {
1800  int containing_component{NO_INDEX};
1801  int nearest_cell{NO_INDEX};
1802  mpq_class dist_to_cell;
1803 
1804  ComponentContainer(int cc, int cell, mpq_class d)
1805  : containing_component(cc), nearest_cell(cell), dist_to_cell(d)
1806  {
1807  }
1808 };
1809 
1816 static Vector<ComponentContainer> find_component_containers(int comp,
1817  const Vector<Vector<int>> &components,
1818  const Array<int> &ambient_cell,
1819  const IMesh &tm,
1820  const PatchesInfo &pinfo,
1821  const TriMeshTopology &tmtopo,
1822  IMeshArena *arena)
1823 {
1824  constexpr int dbg_level = 0;
1825  if (dbg_level > 0) {
1826  std::cout << "FIND_COMPONENT_CONTAINERS for comp " << comp << "\n";
1827  }
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";
1834  }
1835  for (int comp_other : components.index_range()) {
1836  if (comp == comp_other) {
1837  continue;
1838  }
1839  if (dbg_level > 0) {
1840  std::cout << "comp_other = " << comp_other << "\n";
1841  }
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";
1852  }
1853  int close_vert;
1854  int close_edge;
1855  mpq_class d2 = closest_on_tri_to_point(test_v->co_exact,
1856  tri[0]->co_exact,
1857  tri[1]->co_exact,
1858  tri[2]->co_exact,
1859  &close_edge,
1860  &close_vert);
1861  if (dbg_level > 1) {
1862  std::cout << " close_edge=" << close_edge << " close_vert=" << close_vert
1863  << " dsquared=" << d2.get_d() << "\n";
1864  }
1865  if (nearest_tri == NO_INDEX || d2 < nearest_tri_dist_squared) {
1866  nearest_tri = t;
1867  nearest_tri_close_edge = close_edge;
1868  nearest_tri_close_vert = close_vert;
1869  nearest_tri_dist_squared = d2;
1870  }
1871  }
1872  }
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
1879  << ")\n";
1880  }
1881  int containing_cell = find_containing_cell(test_v,
1882  nearest_tri,
1883  nearest_tri_close_edge,
1884  nearest_tri_close_vert,
1885 
1886  pinfo,
1887  tm,
1888  tmtopo,
1889  arena);
1890  if (dbg_level > 0) {
1891  std::cout << "containing cell = " << containing_cell << "\n";
1892  }
1893  if (containing_cell != ambient_cell[comp_other]) {
1894  ans.append(ComponentContainer(comp_other, containing_cell, nearest_tri_dist_squared));
1895  }
1896  }
1897  return ans;
1898 }
1899 
1907 static void finish_patch_cell_graph(const IMesh &tm,
1908  CellsInfo &cinfo,
1909  PatchesInfo &pinfo,
1910  const TriMeshTopology &tmtopo,
1911  IMeshArena *arena)
1912 {
1913  constexpr int dbg_level = 0;
1914  if (dbg_level > 0) {
1915  std::cout << "FINISH_PATCH_CELL_GRAPH\n";
1916  }
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";
1921  }
1922  return;
1923  }
1924  if (dbg_level > 0) {
1925  std::cout << "components:\n";
1926  for (int comp : components.index_range()) {
1927  std::cout << comp << ": " << components[comp] << "\n";
1928  }
1929  }
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);
1933  }
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";
1938  }
1939  }
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);
1945  }
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 << "] ";
1953  }
1954  std::cout << "\n";
1955  }
1956  }
1957  if (dbg_level > 1) {
1958  write_obj_cell_patch(tm, cinfo, pinfo, false, "beforemerge");
1959  }
1960  /* For nested components, merge their ambient cell with the nearest containing cell. */
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);
1965  }
1966  else {
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) {
1970  closest = comp_cont[comp][i];
1971  }
1972  }
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";
1978  }
1979  merge_cells(cont_cell, comp_ambient, cinfo, pinfo);
1980  }
1981  }
1982  /* For outer components (not nested in any other component), merge their ambient cells. */
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";
1990  }
1991  merge_cells(merged_ambient, ambient_cell[outer_components[i]], cinfo, pinfo);
1992  }
1993  }
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";
1999  }
2000  }
2001  std::cout << "Patches\n";
2002  for (int i : pinfo.index_range()) {
2003  std::cout << i << ": " << pinfo.patch(i) << "\n";
2004  }
2005  if (dbg_level > 1) {
2006  write_obj_cell_patch(tm, cinfo, pinfo, false, "finished");
2007  }
2008  }
2009 }
2010 
2024 static void propagate_windings_and_in_output_volume(PatchesInfo &pinfo,
2025  CellsInfo &cinfo,
2026  int c_ambient,
2027  BoolOpType op,
2028  int nshapes,
2029  std::function<int(int)> shape_fn)
2030 {
2031  int dbg_level = 0;
2032  if (dbg_level > 0) {
2033  std::cout << "PROPAGATE_WINDINGS, ambient cell = " << c_ambient << "\n";
2034  }
2035  Cell &cell_ambient = cinfo.cell(c_ambient);
2036  cell_ambient.seed_ambient_winding();
2037  /* Use a vector as a queue. It can't grow bigger than number of cells. */
2038  Vector<int> queue;
2039  queue.reserve(cinfo.tot_cell());
2040  int queue_head = 0;
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";
2046  }
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";
2055  }
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);
2061  BLI_assert(shape < nshapes);
2062  UNUSED_VARS_NDEBUG(nshapes);
2063  if (dbg_level > 1) {
2064  std::cout << " representative tri " << t << ": in shape " << shape << "\n";
2065  }
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";
2069  }
2070  queue.append(c_neighbor);
2071  BLI_assert(queue.size() <= cinfo.tot_cell());
2072  }
2073  }
2074  }
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";
2079  }
2080  }
2081 }
2082 
2092 static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding)
2093 {
2094  int nw = winding.size();
2095  BLI_assert(nw > 0);
2096  switch (bool_optype) {
2097  case BoolOpType::Intersect: {
2098  for (int i = 0; i < nw; ++i) {
2099  if (winding[i] == 0) {
2100  return false;
2101  }
2102  }
2103  return true;
2104  }
2105  case BoolOpType::Union: {
2106  for (int i = 0; i < nw; ++i) {
2107  if (winding[i] != 0) {
2108  return true;
2109  }
2110  }
2111  return false;
2112  }
2113  case BoolOpType::Difference: {
2114  /* if nw > 2, make it shape 0 minus the union of the rest. */
2115  if (winding[0] == 0) {
2116  return false;
2117  }
2118  if (nw == 1) {
2119  return true;
2120  }
2121  for (int i = 1; i < nw; ++i) {
2122  if (winding[i] >= 1) {
2123  return false;
2124  }
2125  }
2126  return true;
2127  }
2128  default:
2129  return false;
2130  }
2131 }
2132 
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,
2142  IMeshArena *arena)
2143 {
2144  int dbg_level = 0;
2145  if (dbg_level > 0) {
2146  std::cout << "extract_zero_volume_cell_tris\n";
2147  }
2148  /* Find patches that are adjacent to zero-volume cells. */
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;
2154  }
2155  else {
2156  adj_to_zv[p] = false;
2157  }
2158  }
2159  /* Partition the adj_to_zv patches into stacks. */
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]) {
2164  continue;
2165  }
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;
2171  /* We arbitrarily choose p's above and below directions as above and below for whole stack.
2172  * Triangles in the stack that don't follow that convention are marked with flipped = true.
2173  * The non-zero-volume cell above the whole stack, following this convention, is
2174  * above_stack_cell. The non-zero-volume cell below the whole stack is #below_stack_cell. */
2175  /* First, walk in the above_cell direction from p. */
2176  int pwalk = p;
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()) {
2181  /* In zero-volume cells, the cell should have exactly two patches. */
2182  BLI_assert(cell->patches().size() == 2);
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;
2188  pwalk = pother;
2189  pwalk_patch = &pinfo.patch(pwalk);
2190  c = flip ? pwalk_patch->cell_below : pwalk_patch->cell_above;
2191  cell = &cinfo.cell(c);
2192  }
2193  const Cell *above_stack_cell = cell;
2194  /* Now walk in the below_cell direction from p. */
2195  pwalk = p;
2196  pwalk_patch = &pinfo.patch(pwalk);
2197  c = pwalk_patch->cell_below;
2198  cell = &cinfo.cell(c);
2199  while (cell->zero_volume()) {
2200  BLI_assert(cell->patches().size() == 2);
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;
2206  pwalk = pother;
2207  pwalk_patch = &pinfo.patch(pwalk);
2208  c = flip ? pwalk_patch->cell_above : pwalk_patch->cell_below;
2209  cell = &cinfo.cell(c);
2210  }
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 << " ";
2217  }
2218  std::cout << "\n";
2219  }
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";
2224  }
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";
2231  }
2232  r_tris.append(tm_subdivided.face(t_to_add));
2233  break;
2234  }
2235  }
2236  if (t_to_add == NO_INDEX) {
2237  const Face *f = tm_subdivided.face(pinfo.patch(p).tri(0));
2238  const Face &tri = *f;
2239  /* We need flipped version or else we would have found it above. */
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);
2248  }
2249  }
2250  }
2251 }
2252 
2264 static IMesh extract_from_in_output_volume_diffs(const IMesh &tm_subdivided,
2265  const PatchesInfo &pinfo,
2266  const CellsInfo &cinfo,
2267  IMeshArena *arena)
2268 {
2269  constexpr int dbg_level = 0;
2270  if (dbg_level > 0) {
2271  std::cout << "\nEXTRACT_FROM_FLAG_DIFFS\n";
2272  }
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";
2286  }
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";
2294  }
2295  Face *f = tm_subdivided.face(t);
2296  if (flip) {
2297  Face &tri = *f;
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);
2306  }
2307  else {
2308  out_tris.append(f);
2309  }
2310  }
2311  }
2312  if (any_zero_volume_cell) {
2313  extract_zero_volume_cell_tris(out_tris, tm_subdivided, pinfo, cinfo, arena);
2314  }
2315  return IMesh(out_tris);
2316 }
2317 
2318 static const char *bool_optype_name(BoolOpType op)
2319 {
2320  switch (op) {
2321  case BoolOpType::None:
2322  return "none";
2323  case BoolOpType::Intersect:
2324  return "intersect";
2325  case BoolOpType::Union:
2326  return "union";
2327  case BoolOpType::Difference:
2328  return "difference";
2329  default:
2330  return "<unknown>";
2331  }
2332 }
2333 
2334 static double3 calc_point_inside_tri_db(const Face &tri)
2335 {
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;
2340  return ans;
2341 }
2342 class InsideShapeTestData {
2343  public:
2344  const IMesh &tm;
2345  std::function<int(int)> shape_fn;
2346  int nshapes;
2347  /* A per-shape vector of parity of hits of that shape. */
2348  Array<int> hit_parity;
2349 
2350  InsideShapeTestData(const IMesh &tm, std::function<int(int)> shape_fn, int nshapes)
2351  : tm(tm), shape_fn(shape_fn), nshapes(nshapes)
2352  {
2353  }
2354 };
2355 
2356 static void inside_shape_callback(void *userdata,
2357  int index,
2358  const BVHTreeRay *ray,
2359  BVHTreeRayHit *UNUSED(hit))
2360 {
2361  const int dbg_level = 0;
2362  if (dbg_level > 0) {
2363  std::cout << "inside_shape_callback, index = " << index << "\n";
2364  }
2365  InsideShapeTestData *data = static_cast<InsideShapeTestData *>(userdata);
2366  const Face &tri = *data->tm.face(index);
2367  int shape = data->shape_fn(tri.orig);
2368  if (shape == -1) {
2369  return;
2370  }
2371  float dist;
2372  float fv0[3];
2373  float fv1[3];
2374  float fv2[3];
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]);
2379  }
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";
2384  }
2386  ray->origin, ray->direction, fv0, fv1, fv2, &dist, nullptr, FLT_EPSILON)) {
2387  /* Count parity as +1 if ray is in the same direction as tri's normal,
2388  * and -1 if the directions are opposite. */
2389  double3 o_db{double(ray->origin[0]), double(ray->origin[1]), double(ray->origin[2])};
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";
2393  }
2394  data->hit_parity[shape] += parity;
2395  }
2396 }
2397 
2407 static void test_tri_inside_shapes(const IMesh &tm,
2408  std::function<int(int)> shape_fn,
2409  int nshapes,
2410  int test_t_index,
2411  BVHTree *tree,
2412  Array<float> &in_shape)
2413 {
2414  const int dbg_level = 0;
2415  if (dbg_level > 0) {
2416  std::cout << "test_point_inside_shapes, t_index = " << test_t_index << "\n";
2417  }
2418  Face &tri_test = *tm.face(test_t_index);
2419  int shape = shape_fn(tri_test.orig);
2420  if (shape == -1) {
2421  in_shape.fill(0.0f);
2422  return;
2423  }
2424  double3 test_point = calc_point_inside_tri_db(tri_test);
2425  /* Offset the test point a tiny bit in the tri_test normal direction. */
2426  tri_test.populate_plane(false);
2427  double3 norm = tri_test.plane->norm.normalized();
2428  const double offset_amount = 1e-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";
2434  }
2435  /* Try six test rays almost along orthogonal axes.
2436  * Perturb their directions slightly to make it less likely to hit a seam.
2437  * Ray-cast assumes they have unit length, so use r1 near 1 and
2438  * ra near 0.5, and rb near .01, but normalized so `sqrt(r1^2 + ra^2 + rb^2) == 1`. */
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";
2454  }
2455  BLI_bvhtree_ray_cast_all(tree, co, test_rays[i], 0.0f, FLT_MAX, inside_shape_callback, &data);
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];
2460  }
2461  std::cout << "\n";
2462  }
2463  for (int j = 0; j < nshapes; ++j) {
2464  if (j != shape && data.hit_parity[j] > 0) {
2465  ++count_insides[j];
2466  }
2467  }
2468  data.hit_parity.fill(0);
2469  }
2470  for (int j = 0; j < nshapes; ++j) {
2471  if (j == shape) {
2472  in_shape[j] = 1.0f; /* Let's say a shape is always inside itself. */
2473  }
2474  else {
2475  in_shape[j] = float(count_insides[j]) / float(num_rays);
2476  }
2477  if (dbg_level > 0) {
2478  std::cout << "shape " << j << " inside = " << in_shape[j] << "\n";
2479  }
2480  }
2481 }
2482 
2489 static BVHTree *raycast_tree(const IMesh &tm)
2490 {
2491  BVHTree *tree = BLI_bvhtree_new(tm.face_size(), FLT_EPSILON, 4, 6);
2492  for (int i : tm.face_index_range()) {
2493  const Face *f = tm.face(i);
2494  float t_cos[9];
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]);
2499  }
2500  }
2501  BLI_bvhtree_insert(tree, i, t_cos, 3);
2502  }
2504  return tree;
2505 }
2506 
2511 static bool raycast_test_remove(BoolOpType op, Array<int> &winding, int shape, bool *r_do_flip)
2512 {
2513  constexpr int dbg_level = 0;
2514  /* Find out the "in the output volume" flag for each of the cases of winding[shape] == 0
2515  * and winding[shape] == 1. If the flags are different, this patch should be in the output.
2516  * Also, if this is a Difference and the shape isn't the first one, need to flip the normals.
2517  */
2518  winding[shape] = 0;
2519  bool in_output_volume_0 = apply_bool_op(op, winding);
2520  winding[shape] = 1;
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] << " ";
2528  }
2529  std::cout << "\niv0=" << in_output_volume_0 << ", iv1=" << in_output_volume_1 << "\n";
2530  std::cout << " remove=" << do_remove << ", flip=" << do_flip << "\n";
2531  }
2532  *r_do_flip = do_flip;
2533  return do_remove;
2534 }
2535 
2537 static void raycast_add_flipped(Vector<Face *> &out_faces, Face &tri, IMeshArena *arena)
2538 {
2539 
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);
2546 }
2547 
2556 static IMesh raycast_tris_boolean(const IMesh &tm,
2557  BoolOpType op,
2558  int nshapes,
2559  std::function<int(int)> shape_fn,
2560  IMeshArena *arena)
2561 {
2562  constexpr int dbg_level = 0;
2563  if (dbg_level > 0) {
2564  std::cout << "RAYCAST_TRIS_BOOLEAN\n";
2565  }
2566  IMesh ans;
2567  BVHTree *tree = raycast_tree(tm);
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";
2578  }
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) {
2582  continue;
2583  }
2584  /* The in_shape array has a confidence value for "insideness".
2585  * For most operations, even a hint of being inside
2586  * gives good results, but when shape is a cutter in a Difference
2587  * operation, we want to be pretty sure that the point is inside other_shape.
2588  * E.g., T75827.
2589  * Also, when the operation is intersection, we also want high confidence.
2590  */
2591  bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
2592  op == BoolOpType::Intersect;
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";
2597  }
2598  winding[other_shape] = inside;
2599  }
2600  bool do_flip;
2601  bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
2602  if (!do_remove) {
2603  if (!do_flip) {
2604  out_faces.append(&tri);
2605  }
2606  else {
2607  raycast_add_flipped(out_faces, tri, arena);
2608  }
2609  }
2610  }
2612  ans.set_faces(out_faces);
2613  return ans;
2614 }
2615 
2616 /* This is (sometimes much faster) version of raycast boolean
2617  * that does it per patch rather than per triangle.
2618  * It may fail in cases where raycast_tri_boolean will succeed,
2619  * but the latter can be very slow on huge meshes. */
2620 static IMesh raycast_patches_boolean(const IMesh &tm,
2621  BoolOpType op,
2622  int nshapes,
2623  std::function<int(int)> shape_fn,
2624  const PatchesInfo &pinfo,
2625  IMeshArena *arena)
2626 {
2627  constexpr int dbg_level = 0;
2628  if (dbg_level > 0) {
2629  std::cout << "RAYCAST_PATCHES_BOOLEAN\n";
2630  }
2631  IMesh ans;
2632  BVHTree *tree = raycast_tree(tm);
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);
2639  /* For test triangle, choose one in the middle of patch list
2640  * as the ones near the beginning may be very near other patches. */
2641  int test_t_index = patch.tri(patch.tot_tri() / 2);
2642  Face &tri_test = *tm.face(test_t_index);
2643  /* Assume all triangles in a patch are in the same shape. */
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";
2649  }
2650  if (shape == -1) {
2651  continue;
2652  }
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) {
2656  continue;
2657  }
2658  bool need_high_confidence = (op == BoolOpType::Difference && shape != 0) ||
2659  op == BoolOpType::Intersect;
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";
2664  }
2665  winding[other_shape] = inside;
2666  }
2667  bool do_flip;
2668  bool do_remove = raycast_test_remove(op, winding, shape, &do_flip);
2669  if (!do_remove) {
2670  for (int t : patch.tris()) {
2671  Face *f = tm.face(t);
2672  if (!do_flip) {
2673  out_faces.append(f);
2674  }
2675  else {
2676  raycast_add_flipped(out_faces, *f, arena);
2677  }
2678  }
2679  }
2680  }
2682 
2683  ans.set_faces(out_faces);
2684  return ans;
2685 }
2690 static std::pair<int, int> find_tris_common_edge(const Face &tri1, const Face &tri2)
2691 {
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);
2696  }
2697  }
2698  }
2699  return std::pair<int, int>(-1, -1);
2700 }
2701 
2702 struct MergeEdge {
2704  double len_squared = 0.0;
2705  /* v1 and v2 are the ends of the edge, ordered so that `v1->id < v2->id` */
2706  const Vert *v1 = nullptr;
2707  const Vert *v2 = nullptr;
2708  /* left_face and right_face are indices into #FaceMergeState.face. */
2709  int left_face = -1;
2710  int right_face = -1;
2711  int orig = -1; /* An edge orig index that can be used for this edge. */
2713  bool dissolvable = false;
2715  bool is_intersect = false;
2716 
2717  MergeEdge() = default;
2718 
2719  MergeEdge(const Vert *va, const Vert *vb)
2720  {
2721  if (va->id < vb->id) {
2722  this->v1 = va;
2723  this->v2 = vb;
2724  }
2725  else {
2726  this->v1 = vb;
2727  this->v2 = va;
2728  }
2729  };
2730 };
2731 
2732 struct MergeFace {
2734  Vector<const Vert *> vert;
2736  Vector<int> edge;
2738  int merge_to = -1;
2740  int orig = -1;
2741 };
2742 struct FaceMergeState {
2746  Vector<MergeFace> face;
2751  Vector<MergeEdge> edge;
2756  Map<std::pair<int, int>, int> edge_map;
2757 };
2758 
2759 static std::ostream &operator<<(std::ostream &os, const FaceMergeState &fms)
2760 {
2761  os << "faces:\n";
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 << " ";
2767  }
2768  std::cout << "\n";
2769  std::cout << " edges " << mf.edge << "\n";
2770  std::cout << " merge_to = " << mf.merge_to << "\n";
2771  }
2772  os << "\nedges:\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";
2778  }
2779  return os;
2780 }
2781 
2787 static void init_face_merge_state(FaceMergeState *fms,
2788  const Vector<int> &tris,
2789  const IMesh &tm,
2790  const double3 &norm)
2791 {
2792  constexpr int dbg_level = 0;
2793  /* Reserve enough faces and edges so that neither will have to resize. */
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";
2799  }
2800  for (int t : tris.index_range()) {
2801  MergeFace mf;
2802  const Face &tri = *tm.face(tris[t]);
2803  if (dbg_level > 0) {
2804  std::cout << "process tri = " << &tri << "\n";
2805  }
2806  BLI_assert(tri.plane_populated());
2807  if (double3::dot(norm, tri.plane->norm) <= 0.0) {
2808  if (dbg_level > 0) {
2809  std::cout << "triangle has wrong orientation, skipping\n";
2810  }
2811  continue;
2812  }
2813  mf.vert.append(tri[0]);
2814  mf.vert.append(tri[1]);
2815  mf.vert.append(tri[2]);
2816  mf.orig = tri.orig;
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";
2820  }
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";
2829  }
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";
2844  }
2845  }
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";
2852  }
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";
2856  }
2857  me.dissolvable = false;
2858  me.orig = tri.edge_orig[i];
2859  }
2860  if (me.dissolvable && tri.is_intersect[i]) {
2861  if (dbg_level > 1) {
2862  std::cout << "reassigning dissolvable = false, is_intersect = true\n";
2863  }
2864  me.dissolvable = false;
2865  me.is_intersect = true;
2866  }
2867  /* This face is left or right depending on orientation of edge. */
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
2871  << "\n";
2872  }
2873  BLI_assert(me.left_face == -1);
2874  fms->edge[me_index].left_face = f;
2875  }
2876  else {
2877  if (dbg_level > 1) {
2878  std::cout << "me.v1 != mf.vert[i] so set edge[" << me_index << "].right_face = " << f
2879  << "\n";
2880  }
2881  BLI_assert(me.right_face == -1);
2882  fms->edge[me_index].right_face = f;
2883  }
2884  fms->face[f].edge.append(me_index);
2885  }
2886  }
2887  if (dbg_level > 0) {
2888  std::cout << *fms;
2889  }
2890 }
2891 
2898 static bool dissolve_leaves_valid_bmesh(FaceMergeState *fms,
2899  const MergeEdge &me,
2900  int me_index,
2901  const MergeFace &mf_left,
2902  const MergeFace &mf_right)
2903 {
2904  int a_edge_start = mf_left.edge.first_index_of_try(me_index);
2905  BLI_assert(a_edge_start != -1);
2906  int alen = mf_left.vert.size();
2907  int blen = mf_right.vert.size();
2908  int b_left_face = me.right_face;
2909  bool ok = true;
2910  /* Is there another edge, not me, in A's face, whose right face is B's left? */
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) {
2915  ok = false;
2916  }
2917  }
2918  /* Is there a vert in A, not me.v1 or me.v2, that is also in B?
2919  * One could avoid this O(n^2) algorithm if had a structure
2920  * saying which faces a vertex touches. */
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];
2926  if (a_v == b_v) {
2927  ok = false;
2928  }
2929  }
2930  }
2931  }
2932  return ok;
2933 }
2934 
2942 static void splice_faces(
2943  FaceMergeState *fms, MergeEdge &me, int me_index, MergeFace &mf_left, MergeFace &mf_right)
2944 {
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);
2954  int ai = 0;
2955  while (ai < a_edge_start) {
2956  splice_vert.append(mf_left.vert[ai]);
2957  splice_edge.append(mf_left.edge[ai]);
2958  ++ai;
2959  }
2960  int bi = b_edge_start + 1;
2961  while (bi != b_edge_start) {
2962  if (bi >= blen) {
2963  bi = 0;
2964  if (bi == b_edge_start) {
2965  break;
2966  }
2967  }
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;
2972  }
2973  else {
2974  fms->edge[mf_right.edge[bi]].right_face = me.left_face;
2975  }
2976  ++bi;
2977  }
2978  ai = a_edge_start + 1;
2979  while (ai < alen) {
2980  splice_vert.append(mf_left.vert[ai]);
2981  splice_edge.append(mf_left.edge[ai]);
2982  ++ai;
2983  }
2984  mf_right.merge_to = me.left_face;
2985  mf_left.vert = splice_vert;
2986  mf_left.edge = splice_edge;
2987  me.left_face = -1;
2988  me.right_face = -1;
2989 }
2990 
2998 static void do_dissolve(FaceMergeState *fms)
2999 {
3000  const int dbg_level = 0;
3001  if (dbg_level > 1) {
3002  std::cout << "\nDO_DISSOLVE\n";
3003  }
3004  Vector<int> dissolve_edges;
3005  for (int e : fms->edge.index_range()) {
3006  if (fms->edge[e].dissolvable) {
3007  dissolve_edges.append(e);
3008  }
3009  }
3010  if (dissolve_edges.size() == 0) {
3011  return;
3012  }
3013  /* Things look nicer if we dissolve the longer edges first. */
3014  std::sort(
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);
3017  });
3018  if (dbg_level > 0) {
3019  std::cout << "Sorted dissolvable edges: " << dissolve_edges << "\n";
3020  }
3021  for (int me_index : dissolve_edges) {
3022  MergeEdge &me = fms->edge[me_index];
3023  if (me.left_face == -1 || me.right_face == -1) {
3024  continue;
3025  }
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)) {
3029  continue;
3030  }
3031  if (dbg_level > 0) {
3032  std::cout << "Removing edge " << me_index << "\n";
3033  }
3034  splice_faces(fms, me, me_index, mf_left, mf_right);
3035  if (dbg_level > 1) {
3036  std::cout << "state after removal:\n";
3037  std::cout << *fms;
3038  }
3039  }
3040 }
3041 
3052 static Vector<Face *> merge_tris_for_face(Vector<int> tris,
3053  const IMesh &tm,
3054  const IMesh &imesh_in,
3055  IMeshArena *arena)
3056 {
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";
3061  }
3062  Vector<Face *> ans;
3063  if (tris.size() <= 1) {
3064  if (tris.size() == 1) {
3065  ans.append(tm.face(tris[0]));
3066  }
3067  return ans;
3068  }
3069  bool done = false;
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) {
3073  /* Is this a case where quad with one diagonal remained unchanged?
3074  * Worth special handling because this case will be very common. */
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";
3085  }
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";
3095  }
3096  ans.append(in_face);
3097  done = true;
3098  }
3099  }
3100  }
3101  }
3102  if (done) {
3103  return ans;
3104  }
3105 
3106  double3 first_tri_normal_rev = -first_tri_normal;
3107  for (const double3 &norm : {first_tri_normal, first_tri_normal_rev}) {
3108  FaceMergeState fms;
3109  init_face_merge_state(&fms, tris, tm, norm);
3110  do_dissolve(&fms);
3111  if (dbg_level > 0) {
3112  std::cout << "faces in merged result:\n";
3113  }
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;
3121  }
3122  Face *facep = arena->add_face(mf.vert, mf.orig, e_orig, is_intersect);
3123  ans.append(facep);
3124  if (dbg_level > 0) {
3125  std::cout << " " << facep << "\n";
3126  }
3127  }
3128  }
3129  }
3130  return ans;
3131 }
3132 
3133 static bool approx_in_line(const double3 &a, const double3 &b, const double3 &c)
3134 {
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) < 1e-4;
3139 }
3140 
3147 static Array<bool> find_dissolve_verts(IMesh &imesh_out, int *r_count_dissolve)
3148 {
3149  imesh_out.populate_vert();
3150  /* dissolve[i] will say whether imesh_out.vert(i) can be dissolved. */
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);
3155  }
3156  /* neighbors[i] will be a pair giving the up-to-two neighboring vertices
3157  * of the vertex v in position i of imesh_out.vert.
3158  * If we encounter a third, then v will not be dissolvable. */
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);
3166  BLI_assert(v_index != NO_INDEX);
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) {
3173  /* Already has a neighbor in another face; can't dissolve unless they are the same. */
3174  if (!((n1 == f_n2 && n2 == f_n1) || (n1 == f_n1 && n2 == f_n2))) {
3175  /* Different neighbors, so can't dissolve. */
3176  dissolve[v_index] = false;
3177  }
3178  }
3179  else {
3180  /* These are the first-seen neighbors. */
3181  neighbors[v_index] = std::pair<const Vert *, const Vert *>(n1, n2);
3182  }
3183  }
3184  }
3185  }
3186  int count = 0;
3187  for (int v_out : imesh_out.vert_index_range()) {
3188  if (dissolve[v_out]) {
3189  dissolve[v_out] = false; /* Will set back to true if final condition is satisfied. */
3190  const std::pair<const Vert *, const Vert *> &nbrs = neighbors[v_out];
3191  if (nbrs.first != nullptr) {
3192  BLI_assert(nbrs.second != 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;
3196  ++count;
3197  }
3198  }
3199  }
3200  }
3201  if (r_count_dissolve != nullptr) {
3202  *r_count_dissolve = count;
3203  }
3204  return dissolve;
3205 }
3206 
3212 static void dissolve_verts(IMesh *imesh, const Array<bool> dissolve, IMeshArena *arena)
3213 {
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();
3220  int num_erase = 0;
3221  for (const Vert *v : face) {
3222  int v_index = imesh->lookup_vert(v);
3223  BLI_assert(v_index != NO_INDEX);
3224  if (dissolve[v_index]) {
3225  face_pos_erase.append(true);
3226  ++num_erase;
3227  }
3228  else {
3229  face_pos_erase.append(false);
3230  }
3231  }
3232  if (num_erase > 0) {
3233  any_faces_erased |= imesh->erase_face_positions(f, face_pos_erase, arena);
3234  }
3235  }
3236  imesh->set_dirty_verts();
3237  if (any_faces_erased) {
3238  imesh->remove_null_faces();
3239  }
3240 }
3241 
3253 static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out,
3254  const IMesh &imesh_in,
3255  IMeshArena *arena)
3256 {
3257  const int dbg_level = 0;
3258  if (dbg_level > 1) {
3259  std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n";
3260  }
3261  /* For now: need plane normals for all triangles. */
3262  for (Face *tri : tm_out.faces()) {
3263  tri->populate_plane(false);
3264  }
3265  /* Gather all output triangles that are part of each input face.
3266  * face_output_tris[f] will be indices of triangles in tm_out
3267  * that have f as their original face. */
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);
3274  }
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";
3279  }
3280  }
3281 
3282  /* Merge triangles that we can from face_output_tri to make faces for output.
3283  * face_output_face[f] will be new original const Face *'s that
3284  * make up whatever part of the boolean output remains of input face f. */
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";
3290  }
3291  int num_out_tris_for_face = face_output_tris.size();
3292  if (num_out_tris_for_face == 0) {
3293  continue;
3294  }
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();
3297  }
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();
3305  }
3306  }
3307  IMesh imesh_out(face);
3308  /* Dissolve vertices that were (a) not original; and (b) now have valence 2 and
3309  * are between two other vertices that are exactly in line with them.
3310  * These were created because of triangulation edges that have been dissolved. */
3311  int count_dissolve;
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);
3315  }
3316  if (dbg_level > 1) {
3317  write_obj_mesh(imesh_out, "boolean_post_dissolve");
3318  }
3319 
3320  return imesh_out;
3321 }
3322 
3329 IMesh boolean_trimesh(IMesh &tm_in,
3330  BoolOpType op,
3331  int nshapes,
3332  std::function<int(int)> shape_fn,
3333  bool use_self,
3334  bool hole_tolerant,
3335  IMeshArena *arena)
3336 {
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");
3345  }
3346  }
3347  if (tm_in.face_size() == 0) {
3348  return IMesh(tm_in);
3349  }
3350 # ifdef PERFDEBUG
3351  double start_time = PIL_check_seconds_timer();
3352  std::cout << " boolean_trimesh, timing begins\n";
3353 # endif
3354 
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;
3359  }
3360 # ifdef PERFDEBUG
3361  double intersect_time = PIL_check_seconds_timer();
3362  std::cout << " intersected, time = " << intersect_time - start_time << "\n";
3363 # endif
3364 
3365  /* It is possible for tm_si to be empty if all the input triangles are bogus/degenerate. */
3366  if (tm_si.face_size() == 0 || op == BoolOpType::None) {
3367  return tm_si;
3368  }
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);
3371 # ifdef PERFDEBUG
3372  double topo_time = PIL_check_seconds_timer();
3373  std::cout << " topology built, time = " << topo_time - intersect_time << "\n";
3374 # endif
3375  bool pwn = is_pwn(tm_si, tm_si_topo);
3376 # ifdef PERFDEBUG
3377  double pwn_time = PIL_check_seconds_timer();
3378  std::cout << " pwn checked, time = " << pwn_time - topo_time << "\n";
3379 # endif
3380  IMesh tm_out;
3381  if (!pwn) {
3382  if (dbg_level > 0) {
3383  std::cout << "Input is not PWN, using raycast method\n";
3384  }
3385  if (hole_tolerant) {
3386  tm_out = raycast_tris_boolean(tm_si, op, nshapes, shape_fn, arena);
3387  }
3388  else {
3389  PatchesInfo pinfo = find_patches(tm_si, tm_si_topo);
3390  tm_out = raycast_patches_boolean(tm_si, op, nshapes, shape_fn, pinfo, arena);
3391  }
3392 # ifdef PERFDEBUG
3393  double raycast_time = PIL_check_seconds_timer();
3394  std::cout << " raycast_boolean done, time = " << raycast_time - pwn_time << "\n";
3395 # endif
3396  }
3397  else {
3398  PatchesInfo pinfo = find_patches(tm_si, tm_si_topo);
3399 # ifdef PERFDEBUG
3400  double patch_time = PIL_check_seconds_timer();
3401  std::cout << " patches found, time = " << patch_time - pwn_time << "\n";
3402 # endif
3403  CellsInfo cinfo = find_cells(tm_si, tm_si_topo, pinfo);
3404  if (dbg_level > 0) {
3405  std::cout << "Input is PWN\n";
3406  }
3407 # ifdef PERFDEBUG
3408  double cell_time = PIL_check_seconds_timer();
3409  std::cout << " cells found, time = " << cell_time - pwn_time << "\n";
3410 # endif
3411  finish_patch_cell_graph(tm_si, cinfo, pinfo, tm_si_topo, arena);
3412 # ifdef PERFDEBUG
3413  double finish_pc_time = PIL_check_seconds_timer();
3414  std::cout << " finished patch-cell graph, time = " << finish_pc_time - cell_time << "\n";
3415 # endif
3416  bool pc_ok = patch_cell_graph_ok(cinfo, pinfo);
3417  if (!pc_ok) {
3418  /* TODO: if bad input can lead to this, diagnose the problem. */
3419  std::cout << "Something funny about input or a bug in boolean\n";
3420  return IMesh(tm_in);
3421  }
3422  cinfo.init_windings(nshapes);
3423  int c_ambient = find_ambient_cell(tm_si, nullptr, tm_si_topo, pinfo, arena);
3424 # ifdef PERFDEBUG
3425  double amb_time = PIL_check_seconds_timer();
3426  std::cout << " ambient cell found, time = " << amb_time - finish_pc_time << "\n";
3427 # endif
3428  if (c_ambient == NO_INDEX) {
3429  /* TODO: find a way to propagate this error to user properly. */
3430  std::cout << "Could not find an ambient cell; input not valid?\n";
3431  return IMesh(tm_si);
3432  }
3433  propagate_windings_and_in_output_volume(pinfo, cinfo, c_ambient, op, nshapes, si_shape_fn);
3434 # ifdef PERFDEBUG
3435  double propagate_time = PIL_check_seconds_timer();
3436  std::cout << " windings propagated, time = " << propagate_time - amb_time << "\n";
3437 # endif
3438  tm_out = extract_from_in_output_volume_diffs(tm_si, pinfo, cinfo, arena);
3439 # ifdef PERFDEBUG
3440  double extract_time = PIL_check_seconds_timer();
3441  std::cout << " extracted, time = " << extract_time - propagate_time << "\n";
3442 # endif
3443  if (dbg_level > 0) {
3444  /* Check if output is PWN. */
3445  TriMeshTopology tm_out_topo(tm_out);
3446  if (!is_pwn(tm_out, tm_out_topo)) {
3447  std::cout << "OUTPUT IS NOT PWN!\n";
3448  }
3449  }
3450  }
3451  if (dbg_level > 1) {
3452  write_obj_mesh(tm_out, "boolean_tm_output");
3453  std::cout << "boolean tm output:\n" << tm_out;
3454  }
3455 # ifdef PERFDEBUG
3456  double end_time = PIL_check_seconds_timer();
3457  std::cout << " boolean_trimesh done, total time = " << end_time - start_time << "\n";
3458 # endif
3459  return tm_out;
3460 }
3461 
3462 static void dump_test_spec(IMesh &imesh)
3463 {
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";
3468  }
3469  for (const Face *f : imesh.faces()) {
3470  for (const Vert *fv : *f) {
3471  std::cout << imesh.lookup_vert(fv) << " ";
3472  }
3473  std::cout << "\n";
3474  }
3475 }
3476 
3481 IMesh boolean_mesh(IMesh &imesh,
3482  BoolOpType op,
3483  int nshapes,
3484  std::function<int(int)> shape_fn,
3485  bool use_self,
3486  bool hole_tolerant,
3487  IMesh *imesh_triangulated,
3488  IMeshArena *arena)
3489 {
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");
3497  std::cout << imesh;
3498  if (dbg_level > 2) {
3499  dump_test_spec(imesh);
3500  }
3501  }
3502  }
3503  IMesh *tm_in = imesh_triangulated;
3504  IMesh our_triangulation;
3505 # ifdef PERFDEBUG
3506  double start_time = PIL_check_seconds_timer();
3507  std::cout << "boolean_mesh, timing begins\n";
3508 # endif
3509  if (tm_in == nullptr) {
3510  our_triangulation = triangulate_polymesh(imesh, arena);
3511  tm_in = &our_triangulation;
3512  }
3513 # ifdef PERFDEBUG
3514  double tri_time = PIL_check_seconds_timer();
3515  std::cout << "triangulated, time = " << tri_time - start_time << "\n";
3516 # endif
3517  if (dbg_level > 1) {
3518  write_obj_mesh(*tm_in, "boolean_tm_in");
3519  }
3520  IMesh tm_out = boolean_trimesh(*tm_in, op, nshapes, shape_fn, use_self, hole_tolerant, arena);
3521 # ifdef PERFDEBUG
3522  double bool_tri_time = PIL_check_seconds_timer();
3523  std::cout << "boolean_trimesh done, time = " << bool_tri_time - tri_time << "\n";
3524 # endif
3525  if (dbg_level > 1) {
3526  std::cout << "bool_trimesh_output:\n" << tm_out;
3527  write_obj_mesh(tm_out, "bool_trimesh_output");
3528  }
3529  IMesh ans = polymesh_from_trimesh_with_dissolve(tm_out, imesh, arena);
3530 # ifdef PERFDEBUG
3531  double dissolve_time = PIL_check_seconds_timer();
3532  std::cout << "polymesh from dissolving, time = " << dissolve_time - bool_tri_time << "\n";
3533 # endif
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);
3539  }
3540  }
3541 # ifdef PERFDEBUG
3542  double end_time = PIL_check_seconds_timer();
3543  std::cout << "boolean_mesh done, total time = " << end_time - start_time << "\n";
3544 # endif
3545  return ans;
3546 }
3547 
3548 } // namespace blender::meshintersect
3549 
3550 #endif // WITH_GMP
typedef float(TangentPoint)[2]
#define BLI_assert(a)
Definition: BLI_assert.h:58
void BLI_bvhtree_balance(BVHTree *tree)
Definition: BLI_kdopbvh.c:956
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
Definition: BLI_kdopbvh.c:873
void BLI_bvhtree_free(BVHTree *tree)
Definition: BLI_kdopbvh.c:945
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
Definition: BLI_kdopbvh.c:998
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)
Definition: BLI_kdopbvh.c:2066
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)
Definition: math_geom.c:1830
const char * BLI_getenv(const char *env) ATTR_NONNULL(1)
Definition: path_util.c:1313
#define UNUSED_VARS_NDEBUG(...)
#define UNUSED(x)
#define ELEM(...)
typedef double(DMatrix)[4][4]
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:126
_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)
Definition: btDbvt.cpp:52
DBVT_INLINE bool Intersect(const btDbvtAabbMm &a, const btDbvtAabbMm &b)
Definition: btDbvt.h:621
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.
Definition: btMatrix3x3.h:157
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)
void * tree
IconTextureDrawCall normal
int count
struct Edge Edge
static unsigned c
Definition: RandGen.cpp:97
static unsigned a[3]
Definition: RandGen.cpp:92
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)
Definition: BLI_hash.hh:214
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
#define hash
Definition: noise.c:169
unsigned __int64 uint64_t
Definition: stdint.h:93
float co[3]
Definition: bmesh_class.h:99
float origin[3]
Definition: BLI_kdopbvh.h:70
float direction[3]
Definition: BLI_kdopbvh.h:72
static double dot(const double3 &a, const double3 &b)
Definition: BLI_double3.hh:195
double PIL_check_seconds_timer(void)
Definition: time.c:80
__forceinline avxf cross(const avxf &a, const avxf &b)
Definition: util_avxf.h:119
__forceinline const avxi abs(const avxi &a)
Definition: util_avxi.h:186
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)