52static void perfdata_init();
53static void incperfcount(
int countnum);
54static void bumpperfcount(
int countnum,
int amt);
55static void doperfmax(
int maxnum,
int val);
56static void dump_perfdata();
60static constexpr bool intersect_use_threading =
true;
62Vert::Vert(
const mpq3 &mco,
const double3 &dco,
int id,
int orig)
63 : co_exact(mco), co(dco), id(id), orig(orig)
67bool Vert::operator==(
const Vert &other)
const
69 return this->co_exact == other.co_exact;
77 x = (
x >> 56) ^ (
x >> 46) ^
x;
78 y = (
y >> 55) ^ (
y >> 45) ^
y;
79 z = (
z >> 54) ^ (
z >> 44) ^
z;
83std::ostream &
operator<<(std::ostream &os,
const Vert *
v)
85 constexpr int dbg_level = 0;
87 if (
v->orig != NO_INDEX) {
92 os <<
"=" <<
v->co_exact;
97bool Plane::operator==(
const Plane &other)
const
99 return norm_exact == other.norm_exact && d_exact == other.d_exact;
102void Plane::make_canonical()
104 if (norm_exact[0] != 0) {
105 mpq_class den = norm_exact[0];
106 norm_exact = mpq3(1, norm_exact[1] / den, norm_exact[2] / den);
107 d_exact = d_exact / den;
109 else if (norm_exact[1] != 0) {
110 mpq_class den = norm_exact[1];
111 norm_exact = mpq3(0, 1, norm_exact[2] / den);
112 d_exact = d_exact / den;
115 if (norm_exact[2] != 0) {
116 mpq_class den = norm_exact[2];
117 norm_exact = mpq3(0, 0, 1);
118 d_exact = d_exact / den;
125 norm =
double3(norm_exact[0].get_d(), norm_exact[1].get_d(), norm_exact[2].get_d());
129Plane::Plane(
const mpq3 &norm_exact,
const mpq_class &d_exact)
130 : norm_exact(norm_exact), d_exact(d_exact)
132 norm =
double3(norm_exact[0].get_d(), norm_exact[1].get_d(), norm_exact[2].get_d());
136Plane::Plane(
const double3 &
norm,
const double d) :
norm(
norm), d(d)
138 norm_exact = mpq3(0, 0, 0);
141bool Plane::exact_populated()
const
143 return norm_exact[0] != 0 || norm_exact[1] != 0 || norm_exact[2] != 0;
152 x = (
x >> 56) ^ (
x >> 46) ^
x;
153 y = (
y >> 55) ^ (
y >> 45) ^
y;
154 z = (
z >> 54) ^ (
z >> 44) ^
z;
155 d = (d >> 53) ^ (d >> 43) ^ d;
156 return x ^
y ^
z ^ d;
159std::ostream &
operator<<(std::ostream &os,
const Plane *plane)
161 os <<
"[" << plane->norm <<
";" << plane->d <<
"]";
167 : vert(
verts), edge_orig(edge_origs), is_intersect(is_intersect), id(id), orig(orig)
173void Face::populate_plane(
bool need_exact)
175 if (plane !=
nullptr) {
176 if (!need_exact || plane->exact_populated()) {
182 if (vert.size() > 3) {
184 for (
int i : index_range()) {
185 co[
i] = vert[
i]->co_exact;
190 mpq3 tr02 = vert[0]->co_exact - vert[2]->co_exact;
191 mpq3 tr12 = vert[1]->co_exact - vert[2]->co_exact;
194 mpq_class d_exact = -
math::dot(normal_exact, vert[0]->co_exact);
195 plane =
new Plane(normal_exact, d_exact);
199 if (vert.size() > 3) {
201 for (
int i : index_range()) {
207 double3 tr02 = vert[0]->co - vert[2]->co;
208 double3 tr12 = vert[1]->co - vert[2]->co;
211 double d = -
math::dot(normal, vert[0]->co);
212 plane =
new Plane(normal, d);
221bool Face::operator==(
const Face &other)
const
223 if (this->
size() != other.size()) {
226 for (FacePos
i : index_range()) {
229 if (this->vert[
i] != other.vert[
i]) {
236bool Face::cyclic_equal(
const Face &other)
const
238 if (this->
size() != other.size()) {
241 int flen = this->
size();
242 for (FacePos start : index_range()) {
243 for (FacePos start_other : index_range()) {
245 for (
int i = 0; ok &&
i < flen; ++
i) {
246 FacePos p = (start +
i) % flen;
247 FacePos p_other = (start_other +
i) % flen;
248 if (this->vert[p] != other.vert[p_other]) {
260std::ostream &
operator<<(std::ostream &os,
const Face *f)
262 os <<
"f" << f->id <<
"o" << f->orig <<
"[";
263 for (
const Vert *
v : *f) {
265 if (
v != f->vert[f->size() - 1]) {
270 if (f->orig != NO_INDEX) {
271 os <<
"o" << f->orig;
274 for (
int i : f->index_range()) {
275 os << f->edge_orig[
i];
276 if (f->is_intersect[
i]) {
279 if (
i != f->size() - 1) {
294class IMeshArena::IMeshArenaImpl : NonCopyable, NonMovable {
304 VSetKey(Vert *p) : vert(p) {}
313 return *this->vert == *other.vert;
324 Vector<std::unique_ptr<Vert>> allocated_verts_;
325 Vector<std::unique_ptr<Face>> allocated_faces_;
328 int next_vert_id_ = 0;
329 int next_face_id_ = 0;
335 void reserve(
int vert_num_hint,
int face_num_hint)
338 allocated_verts_.
reserve(vert_num_hint);
339 allocated_faces_.
reserve(face_num_hint);
342 int tot_allocated_verts()
const
344 return allocated_verts_.
size();
347 int tot_allocated_faces()
const
349 return allocated_faces_.
size();
352 const Vert *add_or_find_vert(
const mpq3 &co,
int orig)
354 double3 dco(co[0].get_d(), co[1].get_d(), co[2].get_d());
355 return add_or_find_vert(co, dco, orig);
358 const Vert *add_or_find_vert(
const double3 &co,
int orig)
360 mpq3 mco(co[0], co[1], co[2]);
361 return add_or_find_vert(mco, co, orig);
364 const Vert *add_or_find_vert(Vert *vert)
366 return add_or_find_vert_(vert);
369 Face *add_face(Span<const Vert *>
verts,
int orig, Span<int> edge_origs, Span<bool> is_intersect)
371 Face *f =
new Face(
verts, next_face_id_++, orig, edge_origs, is_intersect);
372 std::lock_guard
lock(mutex_);
373 allocated_faces_.
append(std::unique_ptr<Face>(f));
377 Face *add_face(Span<const Vert *>
verts,
int orig, Span<int> edge_origs)
379 Array<bool> is_intersect(
verts.size(),
false);
380 return add_face(
verts, orig, edge_origs, is_intersect);
383 Face *add_face(Span<const Vert *>
verts,
int orig)
385 Array<int> edge_origs(
verts.size(), NO_INDEX);
386 Array<bool> is_intersect(
verts.size(),
false);
387 return add_face(
verts, orig, edge_origs, is_intersect);
390 const Vert *find_vert(
const mpq3 &co)
392 Vert vtry(co,
double3(co[0].get_d(), co[1].get_d(), co[2].get_d()), NO_INDEX, NO_INDEX);
393 VSetKey vskey(&vtry);
394 std::lock_guard
lock(mutex_);
407 const Face *find_face(Span<const Vert *> vs)
409 Array<int> eorig(vs.
size(), NO_INDEX);
410 Array<bool> is_intersect(vs.
size(),
false);
411 Face ftry(vs, NO_INDEX, NO_INDEX, eorig, is_intersect);
413 if (ftry.cyclic_equal(*allocated_faces_[
i])) {
414 return allocated_faces_[
i].get();
421 const Vert *add_or_find_vert(
const mpq3 &mco,
const double3 &dco,
int orig)
423 Vert *vtry =
new Vert(mco, dco, NO_INDEX, NO_INDEX);
426 std::lock_guard
lock(mutex_);
429 vtry->id = next_vert_id_++;
433 allocated_verts_.
append(std::unique_ptr<Vert>(vskey.vert));
448 const Vert *add_or_find_vert_(Vert *vtry)
452 std::lock_guard
lock(mutex_);
455 vtry->id = next_vert_id_++;
458 allocated_verts_.
append(std::unique_ptr<Vert>(vskey.vert));
474IMeshArena::IMeshArena()
476 pimpl_ = std::make_unique<IMeshArenaImpl>();
479IMeshArena::~IMeshArena() =
default;
481void IMeshArena::reserve(
int vert_num_hint,
int face_num_hint)
483 pimpl_->reserve(vert_num_hint, face_num_hint);
486int IMeshArena::tot_allocated_verts()
const
488 return pimpl_->tot_allocated_verts();
491int IMeshArena::tot_allocated_faces()
const
493 return pimpl_->tot_allocated_faces();
496const Vert *IMeshArena::add_or_find_vert(
const mpq3 &co,
int orig)
498 return pimpl_->add_or_find_vert(co, orig);
501const Vert *IMeshArena::add_or_find_vert(Vert *vert)
503 return pimpl_->add_or_find_vert(vert);
511 return pimpl_->add_face(
verts, orig, edge_origs, is_intersect);
516 return pimpl_->add_face(
verts, orig, edge_origs);
521 return pimpl_->add_face(
verts, orig);
524const Vert *IMeshArena::add_or_find_vert(
const double3 &co,
int orig)
526 return pimpl_->add_or_find_vert(co, orig);
529const Vert *IMeshArena::find_vert(
const mpq3 &co)
const
531 return pimpl_->find_vert(co);
536 return pimpl_->find_face(
verts);
544int IMesh::lookup_vert(
const Vert *
v)
const
547 return vert_to_index_.lookup_default(
v, NO_INDEX);
550void IMesh::populate_vert()
554 constexpr int ESTIMATE_VERTS_PER_FACE = 4;
555 int estimate_verts_num = ESTIMATE_VERTS_PER_FACE * face_.size();
556 populate_vert(estimate_verts_num);
559void IMesh::populate_vert(
int max_verts)
561 if (vert_populated_) {
564 vert_to_index_.reserve(max_verts);
565 int next_allocate_index = 0;
566 for (
const Face *f : face_) {
567 for (
const Vert *
v : *f) {
570 int index = vert_to_index_.lookup_default(
v, NO_INDEX);
571 if (index == NO_INDEX) {
573 vert_to_index_.add(
v, next_allocate_index++);
577 int tot_v = next_allocate_index;
579 for (
auto item : vert_to_index_.items()) {
580 int index = item.value;
582 vert_[index] = item.key;
587 const bool fix_order =
true;
590 if (a->orig != NO_INDEX && b->orig != NO_INDEX) {
591 return a->orig < b->orig;
593 if (a->orig != NO_INDEX) {
596 if (
b->orig != NO_INDEX) {
599 return a->id <
b->id;
601 for (
int i : vert_.index_range()) {
602 const Vert *
v = vert_[
i];
603 vert_to_index_.add_overwrite(
v,
i);
606 vert_populated_ =
true;
609bool IMesh::erase_face_positions(
int f_index,
Span<bool> face_pos_erase, IMeshArena *arena)
611 const Face *cur_f = this->face(f_index);
612 int cur_len = cur_f->size();
613 int to_erase_num = 0;
614 for (
int i : cur_f->index_range()) {
615 if (face_pos_erase[
i]) {
619 if (to_erase_num == 0) {
622 int new_len = cur_len - to_erase_num;
630 this->face_[f_index] =
nullptr;
637 for (
int i : cur_f->index_range()) {
638 if (!face_pos_erase[
i]) {
639 new_vert[new_index] = (*cur_f)[
i];
640 new_edge_orig[new_index] = cur_f->edge_orig[
i];
641 new_is_intersect[new_index] = cur_f->is_intersect[
i];
646 this->face_[f_index] = arena->add_face(new_vert, cur_f->orig, new_edge_orig, new_is_intersect);
650void IMesh::remove_null_faces()
653 for (Face *f : this->face_) {
658 if (nullcount == 0) {
661 int64_t new_size = this->face_.size() - nullcount;
665 while (copy_from_index < face_.size()) {
666 Face *f_from = face_[copy_from_index++];
668 new_face[copy_to_index++] = f_from;
671 this->face_ = new_face;
674std::ostream &
operator<<(std::ostream &os,
const IMesh &mesh)
676 if (mesh.has_verts()) {
679 for (
const Vert *
v : mesh.vertices()) {
680 os <<
i <<
": " <<
v <<
"\n";
686 for (
const Face *f : mesh.faces()) {
687 os <<
i <<
": " << f <<
"\n";
688 if (f->plane !=
nullptr) {
689 os <<
" plane=" << f->plane <<
" eorig=[";
690 for (Face::FacePos p = 0; p < f->size(); ++p) {
691 os << f->edge_orig[p] <<
" ";
712 double max_abs_val = 0.0;
717 Array<BoundingBox> *face_bounding_box;
719 BBCalcData(
const IMesh &im, Array<BoundingBox> *fbb) : im(im), face_bounding_box(fbb) {};
722static void calc_face_bb_range_func(
void *__restrict userdata,
726 BBCalcData *bbdata =
static_cast<BBCalcData *
>(userdata);
727 double max_abs = 0.0;
728 const Face &face = *bbdata->im.face(iter);
729 BoundingBox &bb = (*bbdata->face_bounding_box)[iter];
730 for (
const Vert *
v : face) {
732 for (
int i = 0;
i < 3; ++
i) {
736 BBChunkData *chunk =
static_cast<BBChunkData *
>(tls->userdata_chunk);
737 chunk->max_abs_val =
max_dd(max_abs, chunk->max_abs_val);
741 Array<BoundingBox> *face_bounding_box;
744 BBPadData(Array<BoundingBox> *fbb,
double pad) : face_bounding_box(fbb),
pad(
pad) {};
747static void pad_face_bb_range_func(
void *__restrict userdata,
751 BBPadData *pad_data =
static_cast<BBPadData *
>(userdata);
752 (*pad_data->face_bounding_box)[iter].expand(pad_data->pad);
755static void calc_face_bb_reduce(
const void *__restrict ,
756 void *__restrict chunk_join,
757 void *__restrict chunk)
759 BBChunkData *bbchunk_join =
static_cast<BBChunkData *
>(chunk_join);
760 BBChunkData *bbchunk =
static_cast<BBChunkData *
>(chunk);
761 bbchunk_join->max_abs_val =
max_dd(bbchunk_join->max_abs_val, bbchunk->max_abs_val);
771 int n = m.face_size();
774 BBCalcData
data(m, &ans);
775 BBChunkData chunk_data;
783 double max_abs_val = chunk_data.max_abs_val;
784 constexpr float pad_factor = 10.0f;
785 float pad = max_abs_val == 0.0f ? FLT_EPSILON : 2 * FLT_EPSILON * max_abs_val;
791 BBPadData pad_data(&ans,
pad);
805class CoplanarCluster {
810 CoplanarCluster() =
default;
813 this->add_tri(t, bb);
826 int tri(
int index)
const
830 const int *
begin()
const
832 return tris_.
begin();
834 const int *end()
const
851class CoplanarClusterInfo {
852 Vector<CoplanarCluster> clusters_;
853 Array<int> tri_cluster_;
856 CoplanarClusterInfo() =
default;
857 explicit CoplanarClusterInfo(
int numtri) : tri_cluster_(
Array<int>(numtri))
859 tri_cluster_.
fill(-1);
862 int tri_cluster(
int t)
const
865 return tri_cluster_[t];
868 int add_cluster(
const CoplanarCluster &cl)
873 tri_cluster_[t] = c_index;
878 int tot_cluster()
const
880 return clusters_.
size();
883 const CoplanarCluster *
begin()
885 return clusters_.
begin();
888 const CoplanarCluster *end()
890 return clusters_.
end();
893 IndexRange index_range()
const
898 const CoplanarCluster &cluster(
int index)
const
901 return clusters_[index];
905static std::ostream &
operator<<(std::ostream &os,
const CoplanarCluster &cl);
907static std::ostream &
operator<<(std::ostream &os,
const CoplanarClusterInfo &clinfo);
909enum ITT_value_kind { INONE, IPOINT, ISEGMENT, ICOPLANAR };
915 enum ITT_value_kind kind = INONE;
917 ITT_value() =
default;
918 explicit ITT_value(ITT_value_kind k) : kind(k) {}
919 ITT_value(ITT_value_kind k,
int tsrc) : t_source(tsrc), kind(k) {}
920 ITT_value(ITT_value_kind k,
const mpq3 &p1) : p1(p1), kind(k) {}
921 ITT_value(ITT_value_kind k,
const mpq3 &p1,
const mpq3 &p2) : p1(p1), p2(p2), kind(k) {}
924static std::ostream &
operator<<(std::ostream &os,
const ITT_value &itt);
931static mpq2 project_3d_to_2d(
const mpq3 &p3d,
int proj_axis)
986static double supremum_dot_cross(
const double3 &a,
const double3 &
b)
993 c[0] = abs_a[1] * abs_b[2] + abs_a[2] * abs_b[1];
994 c[1] = abs_a[2] * abs_b[0] + abs_a[0] * abs_b[2];
995 c[2] = abs_a[0] * abs_b[1] + abs_a[1] * abs_b[0];
1002constexpr int index_dot_plane_coords = 15;
1014constexpr int index_dot_cross = 11;
1024constexpr int index_plane_side = 3 + 2 * index_dot_plane_coords;
1026static int filter_plane_side(
const double3 &p,
1027 const double3 &plane_p,
1028 const double3 &plane_no,
1029 const double3 &abs_p,
1030 const double3 &abs_plane_p,
1031 const double3 &abs_plane_no)
1033 double d =
math::dot(p - plane_p, plane_no);
1037 double supremum =
math::dot(abs_p + abs_plane_p, abs_plane_no);
1038 double err_bound = supremum * index_plane_side * DBL_EPSILON;
1039 if (
fabs(d) > err_bound) {
1040 return d > 0 ? 1 : -1;
1061static inline mpq3 tti_interp(
1062 const mpq3 &a,
const mpq3 &
b,
const mpq3 &c,
const mpq3 &n, mpq3 &ab, mpq3 &ac, mpq3 &dotbuf)
1068 mpq_class den = math::dot_with_buffer(ab, n, dotbuf);
1070 mpq_class alpha = math::dot_with_buffer(ac, n, dotbuf) / den;
1071 return a - alpha * ab;
1081static inline int tti_above(
const mpq3 &a,
1095 n.x = ba.y * ca.z - ba.z * ca.y;
1096 n.y = ba.z * ca.x - ba.x * ca.z;
1097 n.z = ba.x * ca.y - ba.y * ca.x;
1099 return sgn(math::dot_with_buffer(ad, n, dotbuf));
1115static ITT_value itt_canon2(
const mpq3 &p1,
1124 constexpr int dbg_level = 0;
1125 if (dbg_level > 0) {
1126 std::cout <<
"\ntri_tri_intersect_canon:\n";
1127 std::cout <<
"p1=" << p1 <<
" q1=" << q1 <<
" r1=" << r1 <<
"\n";
1128 std::cout <<
"p2=" << p2 <<
" q2=" << q2 <<
" r2=" << r2 <<
"\n";
1129 std::cout <<
"n1=" << n1 <<
" n2=" << n2 <<
"\n";
1130 std::cout <<
"approximate values:\n";
1131 std::cout <<
"p1=(" << p1[0].get_d() <<
"," << p1[1].get_d() <<
"," << p1[2].get_d() <<
")\n";
1132 std::cout <<
"q1=(" << q1[0].get_d() <<
"," << q1[1].get_d() <<
"," << q1[2].get_d() <<
")\n";
1133 std::cout <<
"r1=(" << r1[0].get_d() <<
"," << r1[1].get_d() <<
"," << r1[2].get_d() <<
")\n";
1134 std::cout <<
"p2=(" << p2[0].get_d() <<
"," << p2[1].get_d() <<
"," << p2[2].get_d() <<
")\n";
1135 std::cout <<
"q2=(" << q2[0].get_d() <<
"," << q2[1].get_d() <<
"," << q2[2].get_d() <<
")\n";
1136 std::cout <<
"r2=(" << r2[0].get_d() <<
"," << r2[1].get_d() <<
"," << r2[2].get_d() <<
")\n";
1137 std::cout <<
"n1=(" << n1[0].get_d() <<
"," << n1[1].get_d() <<
"," << n1[2].get_d() <<
")\n";
1138 std::cout <<
"n2=(" << n2[0].get_d() <<
"," << n2[1].get_d() <<
"," << n2[2].get_d() <<
")\n";
1140 mpq3 p1p2 = p2 - p1;
1144 bool no_overlap =
false;
1146 if (tti_above(p1, q1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) {
1148 if (tti_above(p1, r1, r2, p1p2, buf[0], buf[1], buf[2], buf[3]) <= 0) {
1150 if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) > 0) {
1152 if (dbg_level > 0) {
1153 std::cout <<
"overlap [k [i l] j]\n";
1156 intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]);
1157 intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]);
1161 if (dbg_level > 0) {
1162 std::cout <<
"overlap [i [k l] j]\n";
1165 intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]);
1166 intersect_2 = tti_interp(p2, r2, p1, n1, buf[0], buf[1], buf[2]);
1171 if (dbg_level > 0) {
1172 std::cout <<
"no overlap: [k l] [i j]\n";
1179 if (tti_above(p1, q1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) < 0) {
1181 if (dbg_level > 0) {
1182 std::cout <<
"no overlap: [i j] [k l]\n";
1188 if (tti_above(p1, r1, q2, p1p2, buf[0], buf[1], buf[2], buf[3]) >= 0) {
1190 if (dbg_level > 0) {
1191 std::cout <<
"overlap [k [i j] l]\n";
1194 intersect_1 = tti_interp(p1, r1, p2, n2, buf[0], buf[1], buf[2]);
1195 intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]);
1199 if (dbg_level > 0) {
1200 std::cout <<
"overlap [i [k j] l]\n";
1203 intersect_1 = tti_interp(p2, q2, p1, n1, buf[0], buf[1], buf[2]);
1204 intersect_2 = tti_interp(p1, q1, p2, n2, buf[0], buf[1], buf[2]);
1209 return ITT_value(INONE);
1211 if (intersect_1 == intersect_2) {
1212 if (dbg_level > 0) {
1213 std::cout <<
"single intersect: " << intersect_1 <<
"\n";
1215 return ITT_value(IPOINT, intersect_1);
1217 if (dbg_level > 0) {
1218 std::cout <<
"intersect segment: " << intersect_1 <<
", " << intersect_2 <<
"\n";
1220 return ITT_value(ISEGMENT, intersect_1, intersect_2);
1225static ITT_value itt_canon1(
const mpq3 &p1,
1237 constexpr int dbg_level = 0;
1240 return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2);
1243 return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2);
1245 return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2);
1249 return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2);
1252 return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2);
1254 return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2);
1258 return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2);
1260 return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2);
1264 return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2);
1266 return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2);
1269 return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2);
1272 return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2);
1274 if (dbg_level > 0) {
1275 std::cout <<
"triangles are co-planar\n";
1277 return ITT_value(ICOPLANAR);
1280static ITT_value intersect_tri_tri(
const IMesh &tm,
int t1,
int t2)
1282 constexpr int dbg_level = 0;
1286 const Face &tri1 = *tm.face(t1);
1287 const Face &tri2 = *tm.face(t2);
1288 BLI_assert(tri1.plane_populated() && tri2.plane_populated());
1289 const Vert *vp1 = tri1[0];
1290 const Vert *vq1 = tri1[1];
1291 const Vert *vr1 = tri1[2];
1292 const Vert *vp2 = tri2[0];
1293 const Vert *vq2 = tri2[1];
1294 const Vert *vr2 = tri2[2];
1295 if (dbg_level > 0) {
1296 std::cout <<
"\nINTERSECT_TRI_TRI t1=" << t1 <<
", t2=" << t2 <<
"\n";
1297 std::cout <<
" p1 = " << vp1 <<
"\n";
1298 std::cout <<
" q1 = " << vq1 <<
"\n";
1299 std::cout <<
" r1 = " << vr1 <<
"\n";
1300 std::cout <<
" p2 = " << vp2 <<
"\n";
1301 std::cout <<
" q2 = " << vq2 <<
"\n";
1302 std::cout <<
" r2 = " << vr2 <<
"\n";
1310 const double3 &d_p1 = vp1->co;
1311 const double3 &d_q1 = vq1->co;
1312 const double3 &d_r1 = vr1->co;
1313 const double3 &d_p2 = vp2->co;
1314 const double3 &d_q2 = vq2->co;
1315 const double3 &d_r2 = vr2->co;
1316 const double3 &d_n2 = tri2.plane->norm;
1324 int sp1 = filter_plane_side(d_p1, d_r2, d_n2, abs_d_p1, abs_d_r2, abs_d_n2);
1325 int sq1 = filter_plane_side(d_q1, d_r2, d_n2, abs_d_q1, abs_d_r2, abs_d_n2);
1326 int sr1 = filter_plane_side(d_r1, d_r2, d_n2, abs_d_r1, abs_d_r2, abs_d_n2);
1327 if ((sp1 > 0 && sq1 > 0 && sr1 > 0) || (sp1 < 0 && sq1 < 0 && sr1 < 0)) {
1331 if (dbg_level > 0) {
1332 std::cout <<
"no intersection, all t1's verts above or below t2\n";
1334 return ITT_value(INONE);
1337 const double3 &d_n1 = tri1.plane->norm;
1342 int sp2 = filter_plane_side(d_p2, d_r1, d_n1, abs_d_p2, abs_d_r1, abs_d_n1);
1343 int sq2 = filter_plane_side(d_q2, d_r1, d_n1, abs_d_q2, abs_d_r1, abs_d_n1);
1344 int sr2 = filter_plane_side(d_r2, d_r1, d_n1, abs_d_r2, abs_d_r1, abs_d_n1);
1345 if ((sp2 > 0 && sq2 > 0 && sr2 > 0) || (sp2 < 0 && sq2 < 0 && sr2 < 0)) {
1349 if (dbg_level > 0) {
1350 std::cout <<
"no intersection, all t2's verts above or below t1\n";
1352 return ITT_value(INONE);
1356 const mpq3 &p1 = vp1->co_exact;
1357 const mpq3 &q1 = vq1->co_exact;
1358 const mpq3 &r1 = vr1->co_exact;
1359 const mpq3 &p2 = vp2->co_exact;
1360 const mpq3 &q2 = vq2->co_exact;
1361 const mpq3 &r2 = vr2->co_exact;
1363 const mpq3 &n2 = tri2.plane->norm_exact;
1367 sp1 = sgn(math::dot_with_buffer(buf[0], n2, buf[1]));
1372 sq1 = sgn(math::dot_with_buffer(buf[0], n2, buf[1]));
1377 sr1 = sgn(math::dot_with_buffer(buf[0], n2, buf[1]));
1380 if (dbg_level > 1) {
1381 std::cout <<
" sp1=" << sp1 <<
" sq1=" << sq1 <<
" sr1=" << sr1 <<
"\n";
1384 if ((sp1 * sq1 > 0) && (sp1 * sr1 > 0)) {
1385 if (dbg_level > 0) {
1386 std::cout <<
"no intersection, all t1's verts above or below t2 (exact)\n";
1391 return ITT_value(INONE);
1395 const mpq3 &n1 = tri1.plane->norm_exact;
1399 sp2 = sgn(math::dot_with_buffer(buf[0], n1, buf[1]));
1404 sq2 = sgn(math::dot_with_buffer(buf[0], n1, buf[1]));
1409 sr2 = sgn(math::dot_with_buffer(buf[0], n1, buf[1]));
1412 if (dbg_level > 1) {
1413 std::cout <<
" sp2=" << sp2 <<
" sq2=" << sq2 <<
" sr2=" << sr2 <<
"\n";
1416 if ((sp2 * sq2 > 0) && (sp2 * sr2 > 0)) {
1417 if (dbg_level > 0) {
1418 std::cout <<
"no intersection, all t2's verts above or below t1 (exact)\n";
1423 return ITT_value(INONE);
1432 ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
1435 ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
1438 ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
1443 ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
1446 ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
1449 ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
1455 ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
1458 ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
1463 ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
1466 ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
1471 ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2);
1474 ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2);
1477 if (dbg_level > 0) {
1478 std::cout <<
"triangles are co-planar\n";
1480 ans = ITT_value(ICOPLANAR);
1484 if (ans.kind == ICOPLANAR) {
1489 if (ans.kind != INONE) {
1497 const Plane *t_plane;
1499 Vector<std::pair<int, int>> edge;
1500 Vector<Vector<int>> face;
1502 Vector<int> input_face;
1504 Vector<bool> is_reversed;
1506 CDT_result<mpq_class> cdt_out;
1510 Map<std::pair<int, int>,
int> verts_to_edge;
1517static int prepare_need_vert(CDT_data &cd,
const mpq3 &p3d)
1519 mpq2 p2d = project_3d_to_2d(p3d, cd.proj_axis);
1520 int v = cd.vert.append_and_get_index(p2d);
1531static mpq3 unproject_cdt_vert(
const CDT_data &cd,
const mpq2 &p2d)
1535 BLI_assert(cd.t_plane->norm_exact[cd.proj_axis] != 0);
1536 const mpq3 &n = cd.t_plane->norm_exact;
1537 const mpq_class &d = cd.t_plane->d_exact;
1538 switch (cd.proj_axis) {
1540 mpq_class
num = n[1] * p2d[0] + n[2] * p2d[1] + d;
1542 p3d[0] =
num / n[0];
1549 mpq_class
num = n[0] * p2d[0] + n[2] * p2d[1] + d;
1551 p3d[1] =
num / n[1];
1558 mpq_class
num = n[0] * p2d[0] + n[1] * p2d[1] + d;
1560 p3d[2] =
num / n[2];
1569static void prepare_need_edge(CDT_data &cd,
const mpq3 &p1,
const mpq3 &p2)
1571 int v1 = prepare_need_vert(cd, p1);
1572 int v2 = prepare_need_vert(cd, p2);
1573 cd.edge.append(std::pair<int, int>(v1,
v2));
1576static void prepare_need_tri(CDT_data &cd,
const IMesh &tm,
int t)
1578 const Face &tri = *tm.face(t);
1579 int v0 = prepare_need_vert(cd, tri[0]->co_exact);
1580 int v1 = prepare_need_vert(cd, tri[1]->co_exact);
1581 int v2 = prepare_need_vert(cd, tri[2]->co_exact);
1586 if (tri.plane->norm_exact[cd.proj_axis] >= 0) {
1587 rev = cd.proj_axis == 1;
1590 rev = cd.proj_axis != 1;
1592 int cd_t = cd.face.append_and_get_index(
Vector<int>());
1593 cd.face[cd_t].append(v0);
1595 cd.face[cd_t].append(
v2);
1596 cd.face[cd_t].append(v1);
1599 cd.face[cd_t].append(v1);
1600 cd.face[cd_t].append(
v2);
1602 cd.input_face.append(t);
1603 cd.is_reversed.append(rev);
1606static CDT_data prepare_cdt_input(
const IMesh &tm,
int t,
const Span<ITT_value> itts)
1610 ans.t_plane = tm.face(t)->plane;
1613 prepare_need_tri(ans, tm, t);
1614 for (
const ITT_value &itt : itts) {
1619 prepare_need_vert(ans, itt.p1);
1623 prepare_need_edge(ans, itt.p1, itt.p2);
1627 prepare_need_tri(ans, tm, itt.t_source);
1635static CDT_data prepare_cdt_input_for_cluster(
const IMesh &tm,
1636 const CoplanarClusterInfo &clinfo,
1642 const CoplanarCluster &cl = clinfo.cluster(c);
1646 ans.t_plane = tm.face(t0)->plane;
1649 for (
const int t : cl) {
1650 prepare_need_tri(ans, tm, t);
1652 for (
const ITT_value &itt : itts) {
1655 prepare_need_vert(ans, itt.p1);
1659 prepare_need_edge(ans, itt.p1, itt.p2);
1670static inline std::pair<int, int> sorted_int_pair(std::pair<int, int> pair)
1672 if (pair.first <= pair.second) {
1675 return std::pair<int, int>(pair.second, pair.first);
1682static void populate_cdt_edge_map(
Map<std::pair<int, int>,
int> &verts_to_edge,
1683 const CDT_result<mpq_class> &cdt_out)
1685 verts_to_edge.reserve(cdt_out.edge.size());
1686 for (
int e : cdt_out.edge.index_range()) {
1687 std::pair<int, int> vpair = sorted_int_pair(cdt_out.edge[
e]);
1689 verts_to_edge.add(vpair,
e);
1696static void do_cdt(CDT_data &cd)
1698 constexpr int dbg_level = 0;
1699 CDT_input<mpq_class> cdt_in;
1703 if (dbg_level > 0) {
1704 std::cout <<
"CDT input\nVerts:\n";
1705 for (
int i : cdt_in.vert.index_range()) {
1706 std::cout <<
"v" <<
i <<
": " << cdt_in.vert[
i] <<
"=(" << cdt_in.vert[
i][0].get_d() <<
","
1707 << cdt_in.vert[
i][1].get_d() <<
")\n";
1709 std::cout <<
"Edges:\n";
1710 for (
int i : cdt_in.edge.index_range()) {
1711 std::cout <<
"e" <<
i <<
": (" << cdt_in.edge[
i].first <<
", " << cdt_in.edge[
i].second
1714 std::cout <<
"Tris\n";
1715 for (
int f : cdt_in.face.index_range()) {
1716 std::cout <<
"f" << f <<
": ";
1717 for (
int j : cdt_in.face[f].index_range()) {
1718 std::cout << cdt_in.face[f][j] <<
" ";
1725 constexpr int make_edge_map_threshold = 15;
1726 if (cd.cdt_out.edge.size() >= make_edge_map_threshold) {
1727 populate_cdt_edge_map(cd.verts_to_edge, cd.cdt_out);
1729 if (dbg_level > 0) {
1730 std::cout <<
"\nCDT result\nVerts:\n";
1731 for (
int i : cd.cdt_out.vert.index_range()) {
1732 std::cout <<
"v" <<
i <<
": " << cd.cdt_out.vert[
i] <<
"=(" << cd.cdt_out.vert[
i][0].get_d()
1733 <<
"," << cd.cdt_out.vert[
i][1].get_d() <<
"\n";
1735 std::cout <<
"Tris\n";
1736 for (
int f : cd.cdt_out.face.index_range()) {
1737 std::cout <<
"f" << f <<
": ";
1738 for (
int j : cd.cdt_out.face[f].index_range()) {
1739 std::cout << cd.cdt_out.face[f][j] <<
" ";
1741 std::cout <<
"orig: ";
1742 for (
int j : cd.cdt_out.face_orig[f].index_range()) {
1743 std::cout << cd.cdt_out.face_orig[f][j] <<
" ";
1747 std::cout <<
"Edges\n";
1748 for (
int e : cd.cdt_out.edge.index_range()) {
1749 std::cout <<
"e" <<
e <<
": (" << cd.cdt_out.edge[
e].first <<
", "
1750 << cd.cdt_out.edge[
e].second <<
") ";
1751 std::cout <<
"orig: ";
1752 for (
int j : cd.cdt_out.edge_orig[
e].index_range()) {
1753 std::cout << cd.cdt_out.edge_orig[
e][j] <<
" ";
1770static int get_cdt_edge_orig(
1771 int i0,
int i1,
const CDT_data &cd,
const IMesh &in_tm,
bool *r_is_intersect)
1773 int foff = cd.cdt_out.face_edge_offset;
1774 *r_is_intersect =
false;
1776 if (cd.verts_to_edge.size() > 0) {
1778 std::pair<int, int> vpair = sorted_int_pair(std::pair<int, int>(i0, i1));
1779 e = cd.verts_to_edge.lookup_default(vpair, NO_INDEX);
1782 for (
int ee : cd.cdt_out.edge.index_range()) {
1783 std::pair<int, int> edge = cd.cdt_out.edge[ee];
1784 if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) {
1790 if (
e == NO_INDEX) {
1797 int face_eorig = NO_INDEX;
1798 bool have_non_face_eorig =
false;
1799 for (
int orig_index : cd.cdt_out.edge_orig[
e]) {
1801 if (orig_index >= foff) {
1802 if (face_eorig == NO_INDEX) {
1803 int in_face_index = (orig_index / foff) - 1;
1804 int pos = orig_index % foff;
1807 int in_tm_face_index = cd.input_face[in_face_index];
1808 BLI_assert(in_tm_face_index < in_tm.face_size());
1809 const Face *facep = in_tm.face(in_tm_face_index);
1811 bool is_rev = cd.is_reversed[in_face_index];
1812 int eorig = is_rev ? facep->edge_orig[2 -
pos] : facep->edge_orig[
pos];
1813 if (eorig != NO_INDEX) {
1819 if (!have_non_face_eorig) {
1820 have_non_face_eorig =
true;
1822 if (face_eorig != NO_INDEX && have_non_face_eorig) {
1828 if (face_eorig != NO_INDEX) {
1831 if (have_non_face_eorig) {
1836 *r_is_intersect =
true;
1847static Face *cdt_tri_as_imesh_face(
1848 int cdt_out_t,
int cdt_in_t,
const CDT_data &cd,
const IMesh &tm, IMeshArena *arena)
1850 const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
1851 int t_orig = tm.face(cd.input_face[cdt_in_t])->orig;
1852 BLI_assert(cdt_out.face[cdt_out_t].size() == 3);
1853 int i0 = cdt_out.face[cdt_out_t][0];
1854 int i1 = cdt_out.face[cdt_out_t][1];
1855 int i2 = cdt_out.face[cdt_out_t][2];
1856 mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]);
1857 mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]);
1858 mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]);
1862 const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX);
1863 const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX);
1864 const Vert *
v2 = arena->add_or_find_vert(v2co, NO_INDEX);
1869 if (cd.is_reversed[cdt_in_t]) {
1870 int oe0 = get_cdt_edge_orig(i0, i2, cd, tm, &is_isect0);
1871 int oe1 = get_cdt_edge_orig(i2, i1, cd, tm, &is_isect1);
1872 int oe2 = get_cdt_edge_orig(i1, i0, cd, tm, &is_isect2);
1873 facep = arena->add_face(
1874 {v0,
v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
1877 int oe0 = get_cdt_edge_orig(i0, i1, cd, tm, &is_isect0);
1878 int oe1 = get_cdt_edge_orig(i1, i2, cd, tm, &is_isect1);
1879 int oe2 = get_cdt_edge_orig(i2, i0, cd, tm, &is_isect2);
1880 facep = arena->add_face(
1881 {v0, v1,
v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2});
1883 facep->populate_plane(
false);
1888static bool is_quad_flip_first_third(
const double3 &v1,
1899 return math::dot(cross_a, cross_b) > 0.0f;
1914static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena)
1917 int flen = f->
size();
1919 if (!f->plane_populated()) {
1920 f->populate_plane(
false);
1922 const double3 &poly_normal = f->plane->norm;
1923 float no[3] = {
float(poly_normal[0]),
float(poly_normal[1]),
float(poly_normal[2])};
1926 const Vert *v0 = (*f)[0];
1927 const Vert *v1 = (*f)[1];
1928 const Vert *
v2 = (*f)[2];
1929 const Vert *v3 = (*f)[3];
1930 int eo_01 = f->edge_orig[0];
1931 int eo_12 = f->edge_orig[1];
1932 int eo_23 = f->edge_orig[2];
1933 int eo_30 = f->edge_orig[3];
1935 if (
UNLIKELY(is_quad_flip_first_third(v0->co, v1->co,
v2->co, v3->co))) {
1936 f0 = arena->add_face({v0, v1, v3}, f->orig, {eo_01, -1, eo_30}, {
false,
false,
false});
1937 f1 = arena->add_face({v1,
v2, v3}, f->orig, {eo_12, eo_23, -1}, {
false,
false,
false});
1940 f0 = arena->add_face({v0, v1,
v2}, f->orig, {eo_01, eo_12, -1}, {
false,
false,
false});
1941 f1 = arena->add_face({v0,
v2, v3}, f->orig, {-1, eo_23, eo_30}, {
false,
false,
false});
1946 float axis_mat[3][3];
1947 float (*projverts)[2];
1949 const int totfilltri = flen - 2;
1954 for (
int j = 0; j < flen; ++j) {
1955 const double3 &dco = (*f)[j]->co;
1962 for (
int t = 0; t < totfilltri; ++t) {
1963 uint *tri = tris[t];
1966 for (
int k = 0; k < 3; k++) {
1968 v[k] = (*f)[tri[k]];
1971 if ((tri[k] + 1) % flen == tri[(k + 1) % 3]) {
1972 eo[k] = f->edge_orig[tri[k]];
1977 ans[t] = arena->add_face(
1978 {
v[0],
v[1],
v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {
false,
false,
false});
2003static Array<Face *> exact_triangulate_poly(Face *f, IMeshArena *arena)
2005 int flen = f->
size();
2008 faces.first().resize(flen);
2009 std::iota(
faces.first().begin(),
faces.first().end(), 0);
2012 if (!f->plane_populated()) {
2013 f->populate_plane(
false);
2015 const double3 &poly_normal = f->plane->norm;
2021 bool rev1 = (axis == 1);
2022 bool rev2 = poly_normal[axis] < 0;
2023 bool rev = rev1 ^ rev2;
2024 for (
int i = 0;
i < flen; ++
i) {
2025 int ii = rev ? flen -
i - 1 :
i;
2026 mpq2 &p2d = in_verts[ii];
2028 for (
int j = 0; j < 3; ++j) {
2030 p2d[k++] = (*f)[ii]->co_exact[j];
2035 CDT_input<mpq_class> cdt_in;
2036 cdt_in.vert = std::move(in_verts);
2037 cdt_in.face = std::move(
faces);
2040 int n_tris = cdt_out.face.size();
2042 for (
int t = 0; t < n_tris; ++t) {
2046 bool needs_steiner =
false;
2047 for (
int i = 0;
i < 3; ++
i) {
2048 i_v_out[
i] = cdt_out.face[t][
i];
2049 if (cdt_out.vert_orig[i_v_out[
i]].is_empty()) {
2050 needs_steiner =
true;
2053 v[
i] = (*f)[cdt_out.vert_orig[i_v_out[
i]][0]];
2055 if (needs_steiner) {
2057 return polyfill_triangulate_poly(f, arena);
2060 populate_cdt_edge_map(verts_to_edge, cdt_out);
2061 int foff = cdt_out.face_edge_offset;
2062 for (
int i = 0;
i < 3; ++
i) {
2063 std::pair<int, int> vpair(i_v_out[
i], i_v_out[(
i + 1) % 3]);
2064 std::pair<int, int> vpair_canon = sorted_int_pair(vpair);
2068 for (
int orig : cdt_out.edge_orig[e_out]) {
2070 int pos = orig % foff;
2072 eo[
i] = f->edge_orig[
pos];
2078 ans[t] = arena->add_face(
2079 {
v[0],
v[2],
v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {
false,
false,
false});
2082 ans[t] = arena->add_face(
2083 {
v[0],
v[1],
v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {
false,
false,
false});
2089static bool face_is_degenerate(
const Face *f)
2091 const Face &face = *f;
2092 const Vert *v0 = face[0];
2093 const Vert *v1 = face[1];
2094 const Vert *
v2 = face[2];
2095 if (v0 == v1 || v0 ==
v2 || v1 ==
v2) {
2102 double err_bound = supremum_dot_cross(dab, dab) * index_dot_cross * DBL_EPSILON;
2103 if (dab_length_squared > err_bound) {
2106 mpq3 a =
v2->co_exact - v0->co_exact;
2107 mpq3
b =
v2->co_exact - v1->co_exact;
2109 if (ab.x == 0 && ab.y == 0 && ab.z == 0) {
2117static bool any_degenerate_tris_fast(
const Array<Face *> &triangulation)
2119 for (
const Face *f : triangulation) {
2120 const Vert *v0 = (*f)[0];
2121 const Vert *v1 = (*f)[1];
2122 const Vert *
v2 = (*f)[2];
2123 if (v0 == v1 || v0 ==
v2 || v1 ==
v2) {
2130 if (da_length_squared == 0.0 || db_length_squared == 0.0) {
2139 double sin_squared_t = dab_length_squared / (da_length_squared * db_length_squared);
2140 if (sin_squared_t < 1
e-8) {
2155static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena)
2161 if (any_degenerate_tris_fast(ans)) {
2162 return exact_triangulate_poly(f, arena);
2167IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena)
2170 constexpr int estimated_tris_per_face = 3;
2171 face_tris.
reserve(estimated_tris_per_face * imesh.face_size());
2172 threading::parallel_for(imesh.face_index_range(), 2048, [&](
IndexRange range) {
2173 for (int i : range) {
2174 Face *f = imesh.face(i);
2175 if (!f->plane_populated() && f->size() >= 4) {
2176 f->populate_plane(false);
2180 for (Face *f : imesh.faces()) {
2182 int flen = f->size();
2188 for (Face *tri : tris) {
2193 return IMesh(face_tris);
2200static IMesh extract_subdivided_tri(
const CDT_data &cd,
2205 const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
2207 for (
int i = 0;
i < cd.input_face.size(); ++
i) {
2208 if (cd.input_face[
i] == t) {
2212 if (t_in_cdt == -1) {
2213 std::cout <<
"Could not find " << t <<
" in cdt input tris\n";
2217 constexpr int inline_buf_size = 20;
2219 for (
int f : cdt_out.face.index_range()) {
2220 if (cdt_out.face_orig[f].contains(t_in_cdt)) {
2221 Face *facep = cdt_tri_as_imesh_face(f, t_in_cdt, cd, in_tm, arena);
2222 faces.append(facep);
2225 return IMesh(
faces);
2239 BVHTree *tree_{
nullptr};
2240 BVHTree *tree_b_{
nullptr};
2241 BVHTreeOverlap *overlap_{
nullptr};
2242 Array<int> first_overlap_;
2243 uint overlap_num_{0};
2247 std::function<int(
int)> shape_fn;
2253 TriOverlaps(
const IMesh &tm,
2254 const Span<BoundingBox> tri_bb,
2256 std::function<
int(
int)> shape_fn,
2259 constexpr int dbg_level = 0;
2260 if (dbg_level > 0) {
2261 std::cout <<
"TriOverlaps construction\n";
2267 bool two_trees_no_self = nshapes == 2 && !use_self;
2268 if (two_trees_no_self) {
2274 shapes.
resize(tm.face_size());
2275 threading::parallel_for(tm.face_index_range(), 2048, [&](IndexRange range) {
2276 for (int t : range) {
2277 shapes[t] = shape_fn(tm.face(t)->orig);
2282 for (
int t : tm.face_index_range()) {
2286 int shape = shapes[t];
2287 if (two_trees_no_self) {
2291 else if (shape == 1) {
2302 if (two_trees_no_self) {
2308 CBData cbdata{tm, shape_fn, nshapes, use_self};
2314 tree_, tree_, &overlap_num_, only_different_shapes, &cbdata);
2320 if (two_trees_no_self) {
2321 overlap_ =
static_cast<BVHTreeOverlap *
>(
2322 MEM_reallocN(overlap_, 2 * overlap_num_ *
sizeof(overlap_[0])));
2323 for (
uint i = 0;
i < overlap_num_; ++
i) {
2327 overlap_num_ += overlap_num_;
2330 std::sort(overlap_, overlap_ + overlap_num_, bvhtreeverlap_cmp);
2331 if (dbg_level > 0) {
2332 std::cout << overlap_num_ <<
" overlaps found:\n";
2333 for (BVHTreeOverlap ov : overlap()) {
2334 std::cout <<
"A: " << ov.indexA <<
", B: " << ov.indexB <<
"\n";
2337 first_overlap_ = Array<int>(tm.face_size(), -1);
2338 for (
int i = 0;
i < int(overlap_num_); ++
i) {
2340 if (first_overlap_[t] == -1) {
2341 first_overlap_[t] =
i;
2364 int first_overlap_index(
int t)
const
2366 return first_overlap_[t];
2370 static bool only_different_shapes(
void *userdata,
int index_a,
int index_b,
int )
2373 return cbdata->tm.face(index_a)->orig != cbdata->tm.face(index_b)->orig;
2380struct OverlapIttsData {
2381 Vector<std::pair<int, int>> intersect_pairs;
2382 Map<std::pair<int, int>, ITT_value> &itt_map;
2386 OverlapIttsData(Map<std::pair<int, int>, ITT_value> &itt_map,
const IMesh &tm, IMeshArena *arena)
2387 : itt_map(itt_map), tm(tm), arena(arena)
2396static std::pair<int, int> canon_int_pair(
int a,
int b)
2401 return std::pair<int, int>(a,
b);
2404static void calc_overlap_itts_range_func(
void *__restrict userdata,
2408 constexpr int dbg_level = 0;
2409 OverlapIttsData *
data =
static_cast<OverlapIttsData *
>(userdata);
2410 std::pair<int, int> tri_pair =
data->intersect_pairs[iter];
2411 int a = tri_pair.first;
2412 int b = tri_pair.second;
2413 if (dbg_level > 0) {
2414 std::cout <<
"calc_overlap_itts_range_func a=" << a <<
", b=" <<
b <<
"\n";
2416 ITT_value itt = intersect_tri_tri(
data->tm, a,
b);
2417 if (dbg_level > 0) {
2418 std::cout <<
"result of intersecting " << a <<
" and " <<
b <<
" = " << itt <<
"\n";
2421 data->itt_map.add_overwrite(tri_pair, itt);
2428static void calc_overlap_itts(
Map<std::pair<int, int>, ITT_value> &itt_map,
2430 const TriOverlaps &ov,
2433 OverlapIttsData
data(itt_map, tm, arena);
2438 std::pair<int, int> key = canon_int_pair(olap.indexA, olap.indexB);
2439 if (!itt_map.contains(key)) {
2440 itt_map.add_new(key, ITT_value());
2441 data.intersect_pairs.append(key);
2444 int tot_intersect_pairs =
data.intersect_pairs.size();
2458static void calc_subdivided_non_cluster_tris(
Array<IMesh> &r_tri_subdivided,
2460 const Map<std::pair<int, int>, ITT_value> &itt_map,
2461 const CoplanarClusterInfo &clinfo,
2462 const TriOverlaps &ov,
2465 const int dbg_level = 0;
2466 if (dbg_level > 0) {
2467 std::cout <<
"\nCALC_SUBDIVIDED_TRIS\n\n";
2470 struct OverlapTriRange {
2476 int overlap_num = overlap.
size();
2477 overlap_tri_range.
reserve(overlap_num);
2478 int overlap_index = 0;
2479 while (overlap_index < overlap_num) {
2480 int t = overlap[overlap_index].indexA;
2481 int i = overlap_index;
2482 while (
i + 1 < overlap_num && overlap[
i + 1].indexA == t) {
2488 if (clinfo.tri_cluster(t) == NO_INDEX) {
2489 int len =
i - overlap_index + 1;
2490 if (!(
len == 1 && overlap[overlap_index].indexB == t)) {
2491 OverlapTriRange range = {t, overlap_index,
len};
2492 overlap_tri_range.
append(range);
2494 bumpperfcount(0,
len);
2498 overlap_index =
i + 1;
2500 int overlap_tri_range_num = overlap_tri_range.
size();
2502 int grain_size = 64;
2504 for (int otr_index : range) {
2505 OverlapTriRange &otr = overlap_tri_range[otr_index];
2506 int t = otr.tri_index;
2507 if (dbg_level > 0) {
2508 std::cout <<
"handling overlap range\nt=" << t <<
" start=" << otr.overlap_start
2509 <<
" len=" << otr.len <<
"\n";
2511 constexpr int inline_capacity = 100;
2512 Vector<ITT_value, inline_capacity> itts(otr.len);
2513 for (int j = otr.overlap_start; j < otr.overlap_start + otr.len; ++j) {
2514 int t_other = overlap[j].indexB;
2515 std::pair<int, int> key = canon_int_pair(t, t_other);
2517 if (itt_map.contains(key)) {
2518 itt = itt_map.lookup(key);
2520 if (itt.kind != INONE) {
2523 if (dbg_level > 0) {
2524 std::cout <<
" tri t" << t_other <<
"; result = " << itt <<
"\n";
2527 if (itts.size() > 0) {
2528 cd_data[otr_index] = prepare_cdt_input(tm, t, itts);
2529 do_cdt(cd_data[otr_index]);
2534 for (
int otr_index : overlap_tri_range.
index_range()) {
2535 CDT_data &cdd = cd_data[otr_index];
2536 if (cdd.vert.size() > 0) {
2537 int t = overlap_tri_range[otr_index].tri_index;
2538 r_tri_subdivided[t] = extract_subdivided_tri(cdd, tm, t, arena);
2539 if (dbg_level > 1) {
2540 std::cout <<
"subdivide output for tri " << t <<
" = " << r_tri_subdivided[t];
2546 threading::parallel_for(tm.face_index_range(), 2048, [&](
IndexRange range) {
2547 for (int t : range) {
2548 if (r_tri_subdivided[t].face_size() == 0 && clinfo.tri_cluster(t) == NO_INDEX) {
2549 r_tri_subdivided[t] = IMesh({tm.face(t)});
2562static void calc_cluster_tris(
Array<IMesh> &tri_subdivided,
2564 const CoplanarClusterInfo &clinfo,
2568 for (
int c : clinfo.index_range()) {
2569 const CoplanarCluster &cl = clinfo.cluster(c);
2570 const CDT_data &cd = cluster_subdivided[c];
2578 int n_cluster_tris = cl.tot_tri();
2579 const CDT_result<mpq_class> &cdt_out = cd.cdt_out;
2580 BLI_assert(cd.input_face.size() == n_cluster_tris);
2582 for (
int cdt_out_t : cdt_out.face.index_range()) {
2583 for (
int cdt_in_t : cdt_out.face_orig[cdt_out_t]) {
2584 Face *f = cdt_tri_as_imesh_face(cdt_out_t, cdt_in_t, cd, tm, arena);
2585 face_vec[cdt_in_t].append(f);
2588 for (
int cdt_in_t : cd.input_face.index_range()) {
2589 int tm_t = cd.input_face[cdt_in_t];
2590 BLI_assert(tri_subdivided[tm_t].face_size() == 0);
2591 tri_subdivided[tm_t] = IMesh(face_vec[cdt_in_t]);
2596static CDT_data calc_cluster_subdivided(
const CoplanarClusterInfo &clinfo,
2599 const TriOverlaps &ov,
2600 const Map<std::pair<int, int>, ITT_value> &itt_map,
2603 constexpr int dbg_level = 0;
2605 const CoplanarCluster &cl = clinfo.cluster(c);
2607 if (dbg_level > 0) {
2608 std::cout <<
"CALC_CLUSTER_SUBDIVIDED for cluster " << c <<
" = " << cl <<
"\n";
2616 if (dbg_level > 0) {
2617 std::cout <<
"find intersects with triangle " << t <<
" of cluster\n";
2619 int first_i = ov.first_overlap_index(t);
2620 if (first_i == -1) {
2623 for (
int i = first_i;
i < ovspan.
size() && ovspan[
i].indexA == t; ++
i) {
2624 int t_other = ovspan[
i].indexB;
2625 if (clinfo.tri_cluster(t_other) != c) {
2626 if (dbg_level > 0) {
2627 std::cout <<
"use intersect(" << t <<
"," << t_other <<
"\n";
2629 std::pair<int, int> key = canon_int_pair(t, t_other);
2630 if (itt_map.contains(key)) {
2631 ITT_value itt = itt_map.lookup(key);
2632 if (!
ELEM(itt.kind, INONE, ICOPLANAR)) {
2634 if (dbg_level > 0) {
2635 std::cout <<
" itt = " << itt <<
"\n";
2643 CDT_data cd_data = prepare_cdt_input_for_cluster(tm, clinfo, c, itts);
2651 for (
const IMesh &m : tri_subdivided) {
2652 tot_tri += m.face_size();
2656 for (
const IMesh &m : tri_subdivided) {
2657 for (Face *f : m.faces()) {
2658 faces[face_index++] = f;
2661 return IMesh(
faces);
2664static CoplanarClusterInfo find_clusters(
const IMesh &tm,
2666 const Map<std::pair<int, int>, ITT_value> &itt_map)
2668 constexpr int dbg_level = 0;
2669 if (dbg_level > 0) {
2670 std::cout <<
"FIND_CLUSTERS\n";
2672 CoplanarClusterInfo ans(tm.face_size());
2675 maybe_coplanar_tris.
reserve(2 * itt_map.size());
2676 for (
auto item : itt_map.items()) {
2677 if (item.value.kind == ICOPLANAR) {
2678 int t1 = item.key.first;
2679 int t2 = item.key.second;
2683 if (dbg_level > 0) {
2684 std::cout <<
"found " << maybe_coplanar_tris.
size() <<
" possible coplanar tris\n";
2686 if (maybe_coplanar_tris.
is_empty()) {
2687 if (dbg_level > 0) {
2688 std::cout <<
"No possible coplanar tris, so no clusters\n";
2697 for (
int t : maybe_coplanar_tris) {
2701 Plane tplane = *tm.face(t)->plane;
2703 tplane.make_canonical();
2704 if (dbg_level > 0) {
2705 std::cout <<
"plane for tri " << t <<
" = " << &tplane <<
"\n";
2710 if (dbg_level > 0) {
2711 std::cout <<
"already has " << curcls.
size() <<
" clusters\n";
2716 for (CoplanarCluster &cl : curcls) {
2717 if (dbg_level > 1) {
2718 std::cout <<
"consider intersecting with cluster " << cl <<
"\n";
2720 if (bbs_might_intersect(tri_bb[t], cl.bounding_box())) {
2721 if (dbg_level > 1) {
2722 std::cout <<
"append to int_cls\n";
2727 if (dbg_level > 1) {
2728 std::cout <<
"append to no_int_cls\n";
2735 if (dbg_level > 1) {
2736 std::cout <<
"no intersecting clusters for t, make a new one\n";
2738 curcls.append(CoplanarCluster(t, tri_bb[t]));
2740 else if (int_cls.
size() == 1) {
2742 if (dbg_level > 1) {
2743 std::cout <<
"exactly one existing cluster, " << int_cls[0] <<
", adding to it\n";
2745 int_cls[0]->add_tri(t, tri_bb[t]);
2750 if (dbg_level > 1) {
2751 std::cout <<
"merging\n";
2753 CoplanarCluster mergecl;
2754 mergecl.add_tri(t, tri_bb[t]);
2755 for (CoplanarCluster *cl : int_cls) {
2757 mergecl.add_tri(t, tri_bb[t]);
2762 for (CoplanarCluster *cl_no_int : no_int_cls) {
2763 newvec.
append(*cl_no_int);
2769 if (dbg_level > 0) {
2770 std::cout <<
"first cluster for its plane\n";
2777 for (
auto item : plane_cls.
items()) {
2778 for (
const CoplanarCluster &cl : item.value) {
2779 if (cl.tot_tri() > 1) {
2780 ans.add_cluster(cl);
2793struct DegenChunkData {
2794 bool has_degenerate_tri =
false;
2797static void degenerate_range_func(
void *__restrict userdata,
2801 DegenData *
data =
static_cast<DegenData *
>(userdata);
2802 DegenChunkData *chunk_data =
static_cast<DegenChunkData *
>(tls->userdata_chunk);
2803 const Face *f =
data->tm.face(iter);
2804 bool is_degenerate = face_is_degenerate(f);
2805 chunk_data->has_degenerate_tri |= is_degenerate;
2808static void degenerate_reduce(
const void *__restrict ,
2809 void *__restrict chunk_join,
2810 void *__restrict chunk)
2812 DegenChunkData *degen_chunk_join =
static_cast<DegenChunkData *
>(chunk_join);
2813 DegenChunkData *degen_chunk =
static_cast<DegenChunkData *
>(chunk);
2814 degen_chunk_join->has_degenerate_tri |= degen_chunk->has_degenerate_tri;
2818static bool has_degenerate_tris(
const IMesh &tm)
2820 DegenData degen_data = {tm};
2821 DegenChunkData degen_chunk_data;
2830 return degen_chunk_data.has_degenerate_tri;
2833static IMesh remove_degenerate_tris(
const IMesh &tm_in)
2837 new_faces.
reserve(tm_in.face_size());
2838 for (Face *f : tm_in.faces()) {
2839 if (!face_is_degenerate(f)) {
2843 ans.set_faces(new_faces);
2847IMesh trimesh_self_intersect(
const IMesh &tm_in, IMeshArena *arena)
2849 return trimesh_nary_intersect(tm_in, 1, [](
int ) {
return 0; },
true, arena);
2852IMesh trimesh_nary_intersect(
const IMesh &tm_in,
2858 constexpr int dbg_level = 0;
2859 if (dbg_level > 0) {
2860 std::cout <<
"\nTRIMESH_NARY_INTERSECT nshapes=" << nshapes <<
" use_self=" << use_self
2862 for (
const Face *f : tm_in.faces()) {
2866 if (dbg_level > 1) {
2867 std::cout <<
"input mesh:\n" << tm_in;
2868 for (
int t : tm_in.face_index_range()) {
2869 std::cout <<
"shape(" << t <<
") = " << shape_fn(tm_in.face(t)->orig) <<
"\n";
2871 write_obj_mesh(
const_cast<IMesh &
>(tm_in),
"trimesh_input");
2877 std::cout <<
"trimesh_nary_intersect start\n";
2881 const IMesh *tm_clean = &tm_in;
2883 if (has_degenerate_tris(tm_in)) {
2884 if (dbg_level > 0) {
2885 std::cout <<
"cleaning degenerate triangles\n";
2887 tm_cleaned = remove_degenerate_tris(tm_in);
2888 tm_clean = &tm_cleaned;
2889 if (dbg_level > 1) {
2890 std::cout <<
"cleaned input mesh:\n" << tm_cleaned;
2895 std::cout <<
"cleaned, time = " << clean_time - start_time <<
"\n";
2900 std::cout <<
"bbs calculated, time = " << bb_calc_time - clean_time <<
"\n";
2902 TriOverlaps tri_ov(*tm_clean, tri_bb, nshapes, shape_fn, use_self);
2905 std::cout <<
"intersect overlaps calculated, time = " << overlap_time - bb_calc_time <<
"\n";
2907 Array<IMesh> tri_subdivided(tm_clean->face_size(), NoInitialization());
2908 threading::parallel_for(tm_clean->face_index_range(), 1024, [&](
IndexRange range) {
2909 for (int t : range) {
2910 if (tri_ov.first_overlap_index(t) != -1) {
2911 tm_clean->face(t)->populate_plane(true);
2913 new (static_cast<void *>(&tri_subdivided[t])) IMesh;
2918 std::cout <<
"planes populated, time = " << plane_populate - overlap_time <<
"\n";
2923 itt_map.
reserve(tri_ov.overlap().size());
2924 calc_overlap_itts(itt_map, *tm_clean, tri_ov, arena);
2927 std::cout <<
"itts found, time = " << itt_time - plane_populate <<
"\n";
2929 CoplanarClusterInfo clinfo = find_clusters(*tm_clean, tri_bb, itt_map);
2930 if (dbg_level > 1) {
2931 std::cout << clinfo;
2935 std::cout <<
"clusters found, time = " << find_cluster_time - itt_time <<
"\n";
2936 doperfmax(0, tm_in.face_size());
2937 doperfmax(1, clinfo.tot_cluster());
2938 doperfmax(2, tri_ov.overlap().size());
2940 calc_subdivided_non_cluster_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena);
2943 std::cout <<
"subdivided non-cluster tris found, time = " << subdivided_tris_time - itt_time
2947 for (
int c : clinfo.index_range()) {
2948 cluster_subdivided[c] = calc_cluster_subdivided(clinfo, c, *tm_clean, tri_ov, itt_map, arena);
2952 std::cout <<
"subdivided clusters found, time = "
2953 << cluster_subdivide_time - subdivided_tris_time <<
"\n";
2955 calc_cluster_tris(tri_subdivided, *tm_clean, clinfo, cluster_subdivided, arena);
2958 std::cout <<
"subdivided cluster tris found, time = " << extract_time - cluster_subdivide_time
2961 IMesh combined = union_tri_subdivides(tri_subdivided);
2962 if (dbg_level > 1) {
2963 std::cout <<
"TRIMESH_NARY_INTERSECT answer:\n";
2964 std::cout << combined;
2968 std::cout <<
"triangles combined, time = " << end_time - extract_time <<
"\n";
2969 std::cout <<
"trimesh_nary_intersect done, total time = " << end_time - start_time <<
"\n";
2975static std::ostream &
operator<<(std::ostream &os,
const CoplanarCluster &cl)
2979 for (
const int t : cl) {
2992static std::ostream &
operator<<(std::ostream &os,
const CoplanarClusterInfo &clinfo)
2994 os <<
"Coplanar Cluster Info:\n";
2995 for (
int c : clinfo.index_range()) {
2996 os << c <<
": " << clinfo.cluster(c) <<
"\n";
3001static std::ostream &
operator<<(std::ostream &os,
const ITT_value &itt)
3008 os <<
"point " << itt.p1;
3011 os <<
"segment " << itt.p1 <<
" " << itt.p2;
3014 os <<
"co-planar t" << itt.t_source;
3020void write_obj_mesh(IMesh &m,
const std::string &objname)
3027 if (objdir ==
nullptr) {
3028 std::cout <<
"Could not access home directory\n";
3032 const char *objdir =
"/tmp/";
3035 if (m.face_size() == 0) {
3039 std::string fname = std::string(objdir) + objname + std::string(
".obj");
3043 std::cout <<
"Could not open file " << fname <<
"\n";
3047 if (!m.has_verts()) {
3050 for (
const Vert *
v : m.vertices()) {
3052 f <<
"v " << dv[0] <<
" " << dv[1] <<
" " << dv[2] <<
"\n";
3054 for (
const Face *face : m.faces()) {
3057 for (
const Vert *
v : *face) {
3058 int i = m.lookup_vert(
v);
3071 Vector<const char *> count_name;
3073 Vector<const char *> max_name;
3076static PerfCounts *perfdata =
nullptr;
3078static void perfdata_init()
3080 perfdata =
new PerfCounts;
3083 perfdata->count.append(0);
3084 perfdata->count_name.append(
"Non-cluster overlaps");
3087 perfdata->count.append(0);
3088 perfdata->count_name.append(
"intersect_tri_tri calls");
3091 perfdata->count.append(0);
3092 perfdata->count_name.append(
"tri tri intersects decided by filter plane tests");
3095 perfdata->count.append(0);
3096 perfdata->count_name.append(
"tri tri intersects decided by exact plane tests");
3099 perfdata->count.append(0);
3100 perfdata->count_name.append(
"final non-NONE intersects");
3103 perfdata->max.append(0);
3104 perfdata->max_name.append(
"total faces");
3107 perfdata->max.append(0);
3108 perfdata->max_name.append(
"total clusters");
3111 perfdata->max.append(0);
3112 perfdata->max_name.append(
"total overlaps");
3115static void incperfcount(
int countnum)
3117 perfdata->count[countnum]++;
3120static void bumpperfcount(
int countnum,
int amt)
3122 perfdata->count[countnum] += amt;
3125static void doperfmax(
int maxnum,
int val)
3127 perfdata->max[maxnum] =
max_ii(perfdata->max[maxnum], val);
3130static void dump_perfdata()
3132 std::cout <<
"\nPERFDATA\n";
3133 for (
int i : perfdata->count.index_range()) {
3134 std::cout << perfdata->count_name[
i] <<
" = " << perfdata->count[
i] <<
"\n";
3136 for (
int i : perfdata->max.index_range()) {
3137 std::cout << perfdata->max_name[
i] <<
" = " << perfdata->max[
i] <<
"\n";
File and directory operations.
const char * BLI_dir_home(void)
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
BVHTreeOverlap * BLI_bvhtree_overlap(const BVHTree *tree1, const BVHTree *tree2, unsigned int *r_overlap_num, BVHTree_OverlapCallback callback, void *userdata)
MINLINE int max_ii(int a, int b)
MINLINE double max_dd(double a, double b)
bool isect_aabb_aabb_v3(const float min1[3], const float max1[3], const float min2[3], const float max2[3])
void axis_dominant_v3_to_m3_negate(float r_mat[3][3], const float normal[3])
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
ATTR_WARN_UNUSED_RESULT const size_t num
void BLI_polyfill_calc(const float(*coords)[2], unsigned int coords_num, int coords_sign, unsigned int(*r_tris)[3])
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
double BLI_time_now_seconds(void)
#define UNUSED_VARS_NDEBUG(...)
std::ostream & operator<<(std::ostream &stream, bUUID uuid)
#define MEM_reallocN(vmemh, len)
bool operator==(const AssetWeakReference &a, const AssetWeakReference &b)
int pad[32 - sizeof(int)]
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
void fill(const T &value) const
bool add_overwrite(const Key &key, const Value &value)
const Value & lookup(const Key &key) const
Value lookup_default(const Key &key, const Value &default_value) const
void add_new(const Key &key, const Value &value)
bool contains(const Key &key) const
ItemIterator items() const &
void reserve(const int64_t n)
void add_new(const Key &key)
const Key * lookup_key_ptr(const Key &key) const
constexpr int64_t size() const
void reserve(const int64_t n)
void add_multiple(Span< Key > keys)
int64_t append_and_get_index(const T &value)
void append(const T &value)
IndexRange index_range() const
void resize(const int64_t new_size)
void reserve(const int64_t min_capacity)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
void MEM_freeN(void *vmemh)
ccl_device_inline float2 fabs(const float2 a)
GAttributeReader lookup(const void *owner, const StringRef name)
T length_squared(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
AxisSigned cross(const AxisSigned a, const AxisSigned b)
int dominant_axis(const VecBase< T, 3 > &a)
VecBase< T, 3 > cross_poly(Span< VecBase< T, 3 > > poly)
CDT_result< double > delaunay_2d_calc(const CDT_input< double > &input, CDT_output_type output_type)
void parallel_sort(RandomAccessIterator begin, RandomAccessIterator end)
VecBase< double, 3 > double3
TaskParallelReduceFunc func_reduce
size_t userdata_chunk_size