OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::anonymous_namespace{MeshRepair.cpp} Namespace Reference

Classes

class  UnionFind
struct  EdgeUse
struct  SoupTopology
struct  BorderLoop
struct  CdtFaceInfo
struct  UnitResult

Typedefs

using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel
using Point = Kernel::Point_3
using Vector = Kernel::Vector_3
using Mesh = CGAL::Surface_mesh<Point>
using Vertex = Mesh::Vertex_index
using Halfedge = Mesh::Halfedge_index
using Face = Mesh::Face_index
using Triangle = std::array<std::size_t, 3>
using Tree = CGAL::AABB_tree<CGAL::AABB_traits_3<Kernel, CGAL::AABB_face_graph_triangle_primitive<Mesh>>>
using Box = CGAL::Box_intersection_d::Box_with_info_d<double, 3, std::size_t>
using CdtVb = CGAL::Triangulation_vertex_base_with_info_2<Vertex, Kernel>
using CdtFb = CGAL::Constrained_triangulation_face_base_2<Kernel, CGAL::Triangulation_face_base_with_info_2<CdtFaceInfo, Kernel>>
using Cdt = CGAL::Constrained_Delaunay_triangulation_2<Kernel, CGAL::Triangulation_data_structure_2<CdtVb, CdtFb>, CGAL::No_constraint_intersection_tag>

Functions

template<class Container, class Mask>
void erase_marked (Container &items, const Mask &marked)
template<class Fn>
void parallel_for_each (std::size_t n, Fn &&fn)
UnionFind connect_vertices (std::size_t num_points, const std::vector< Triangle > &triangles)
bool bbox_contains (const CGAL::Bbox_3 &outer, const CGAL::Bbox_3 &inner, double tolerance=0.)
std::vector< std::pair< std::size_t, std::size_t > > overlapping_boxes (std::vector< Box > &boxes)
double bbox_diagonal (const std::vector< Point > &points)
std::vector< EdgeUse > sorted_edge_uses (const std::vector< Triangle > &triangles)
template<class Fn>
void for_each_edge (const std::vector< EdgeUse > &uses, Fn &&fn)
bool same_direction (const std::vector< Triangle > &triangles, const EdgeUse &e1, const EdgeUse &e2)
SoupTopology analyze_topology (std::size_t num_points, const std::vector< Triangle > &triangles)
void orient_with_cgal (std::vector< Point > &points, std::vector< Triangle > &triangles)
void extract_group (const std::vector< Point > &points, const std::vector< Triangle > &triangles, const std::vector< std::size_t > &group, std::vector< Point > &group_points, std::vector< Triangle > &group_triangles)
std::vector< Mesh > soup_to_meshes (const std::vector< Point > &points, const std::vector< Triangle > &triangles, const std::vector< std::vector< std::size_t > > &groups, bool known_valid)
std::vector< Mesh > split_shells (Mesh &mesh)
std::vector< Mesh > take_closed_shells (const std::vector< Point > &points, std::vector< Triangle > &triangles)
void remove_duplicate_triangles (std::size_t num_points, std::vector< Triangle > &triangles)
void clean_soup (std::vector< Point > &points, std::vector< Triangle > &triangles)
std::size_t weld_border_vertices (const std::vector< Point > &points, std::vector< Triangle > &triangles, const std::vector< std::size_t > &border, double tolerance)
bool orient_soup (std::vector< Point > &points, std::vector< Triangle > &triangles, std::size_t &reversed_parts)
std::size_t keep_input_orientation (Mesh &mesh, const std::vector< Point > &points_before, const std::vector< Triangle > &triangles_before, const std::vector< Point > &points, const std::vector< Triangle > &triangles)
void close_cracks (Mesh &mesh, double tolerance)
void mark_nesting (Cdt &cdt)
bool fill_planar_region (Mesh &mesh, const std::vector< const BorderLoop * > &loops)
void fill_with_fan (Mesh &mesh, const BorderLoop &loop)
std::size_t fill_holes (Mesh &mesh, double tolerance)
double signed_volume (const Mesh &mesh)
std::size_t remove_local_self_intersections (Mesh &shell)
bool surfaces_intersect (const Mesh &mesh, const Tree &other)
Point inner_sample (const Mesh &shell)
void resolve_shells (std::vector< Mesh > &shells, MeshRepairStats &stats, const std::function< void(int)> &progress)
std::vector< std::vector< std::size_t > > soup_units (const std::vector< Point > &points, const std::vector< Triangle > &triangles, const SoupTopology &topology, double tolerance)
UnitResult repair_unit (const std::vector< Point > &points, const std::vector< Triangle > &triangles, const std::vector< std::size_t > &faces, const SoupTopology &topology, double tolerance)
bool repair_soup (std::vector< Point > &points, std::vector< Triangle > &triangles, std::vector< Mesh > &shells, MeshRepairStats &stats, const std::function< void(int)> &progress)
bool repair_its (const indexed_triangle_set &its, indexed_triangle_set &result, MeshRepairStats &stats, const std::function< void(int)> &progress)

Variables

constexpr std::size_t NONE = std::size_t(-1)

Class Documentation

◆ Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology

struct Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology
Collaboration diagram for Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology:
Class Members
vector< char > bad_face
vector< size_t > border
vector< char > open_face
double open_fraction = 0.
vector< char > pinched

◆ Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop

struct Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop
Collaboration diagram for Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop:
Class Members
Bbox_3 bbox
Halfedge halfedge
vector< Halfedge > halfedges
Vector normal
double offset = 0.
bool planar = false

◆ Slic3r::anonymous_namespace{MeshRepair.cpp}::CdtFaceInfo

struct Slic3r::anonymous_namespace{MeshRepair.cpp}::CdtFaceInfo
Class Members
int nesting = -1

◆ Slic3r::anonymous_namespace{MeshRepair.cpp}::UnitResult

struct Slic3r::anonymous_namespace{MeshRepair.cpp}::UnitResult
Collaboration diagram for Slic3r::anonymous_namespace{MeshRepair.cpp}::UnitResult:
Class Members
bool closed = true
size_t filled = 0
size_t reversed = 0
vector< Mesh > shells

Typedef Documentation

◆ Box

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Box = CGAL::Box_intersection_d::Box_with_info_d<double, 3, std::size_t>

◆ Cdt

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Cdt = CGAL::Constrained_Delaunay_triangulation_2<Kernel, CGAL::Triangulation_data_structure_2<CdtVb, CdtFb>, CGAL::No_constraint_intersection_tag>

◆ CdtFb

using Slic3r::anonymous_namespace{MeshRepair.cpp}::CdtFb = CGAL::Constrained_triangulation_face_base_2<Kernel, CGAL::Triangulation_face_base_with_info_2<CdtFaceInfo, Kernel>>

◆ CdtVb

using Slic3r::anonymous_namespace{MeshRepair.cpp}::CdtVb = CGAL::Triangulation_vertex_base_with_info_2<Vertex, Kernel>

◆ Face

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Face = Mesh::Face_index

◆ Halfedge

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Halfedge = Mesh::Halfedge_index

◆ Kernel

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Kernel = CGAL::Exact_predicates_inexact_constructions_kernel

◆ Mesh

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Mesh = CGAL::Surface_mesh<Point>

◆ Point

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Point = Kernel::Point_3

◆ Tree

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Tree = CGAL::AABB_tree<CGAL::AABB_traits_3<Kernel, CGAL::AABB_face_graph_triangle_primitive<Mesh>>>

◆ Triangle

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Triangle = std::array<std::size_t, 3>

◆ Vector

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Vector = Kernel::Vector_3

◆ Vertex

using Slic3r::anonymous_namespace{MeshRepair.cpp}::Vertex = Mesh::Vertex_index

Function Documentation

◆ analyze_topology()

SoupTopology Slic3r::anonymous_namespace{MeshRepair.cpp}::analyze_topology ( std::size_t num_points,
const std::vector< Triangle > & triangles )
218{
219 SoupTopology topology{std::vector<char>(triangles.size(), 0), std::vector<char>(triangles.size(), 0), std::vector<char>(num_points, 0)};
220 const std::vector<EdgeUse> uses = sorted_edge_uses(triangles);
221 UnionFind fans(3 * triangles.size());
222 std::size_t num_edges = 0;
223 for_each_edge(uses, [&](std::size_t i, std::size_t j) {
224 const EdgeUse &e = uses[i];
225 ++num_edges;
226 if (j - i == 1 && e.a != e.b) {
227 topology.open_face[e.corner / 3] = 1;
228 topology.border.push_back(e.a);
229 topology.border.push_back(e.b);
230 } else if (j - i != 2 || e.a == e.b || same_direction(triangles, e, uses[i + 1])) {
231 for (std::size_t k = i; k < j; ++k)
232 topology.bad_face[uses[k].corner / 3] = 1;
233 } else {
234 const std::size_t c = e.corner, n = uses[i + 1].corner;
235 fans.unite(c, n - n % 3 + (n + 1) % 3);
236 fans.unite(c - c % 3 + (c + 1) % 3, n);
237 }
238 });
239 topology.open_fraction = num_edges == 0 ? 0. : double(topology.border.size() / 2) / double(num_edges);
240 std::sort(topology.border.begin(), topology.border.end());
241 topology.border.erase(std::unique(topology.border.begin(), topology.border.end()), topology.border.end());
242
243 std::vector<std::size_t> fan_of(num_points, NONE);
244 for (std::size_t c = 0; c < 3 * triangles.size(); ++c) {
245 const std::size_t v = triangles[c / 3][c % 3], root = fans.find(c);
246 if (fan_of[v] == NONE)
247 fan_of[v] = root;
248 else if (fan_of[v] != root)
249 topology.pinched[v] = 1;
250 }
251 return topology;
252}
void for_each_edge(const std::vector< EdgeUse > &uses, Fn &&fn)
Definition MeshRepair.cpp:191
std::vector< char > open_face
Definition MeshRepair.cpp:209
std::vector< char > pinched
Definition MeshRepair.cpp:211
double open_fraction
Definition MeshRepair.cpp:213
constexpr std::size_t NONE
Definition MeshRepair.cpp:91
std::vector< EdgeUse > sorted_edge_uses(const std::vector< Triangle > &triangles)
Definition MeshRepair.cpp:177
bool same_direction(const std::vector< Triangle > &triangles, const EdgeUse &e1, const EdgeUse &e2)
Definition MeshRepair.cpp:202
std::vector< std::size_t > border
Definition MeshRepair.cpp:212
std::vector< char > bad_face
Definition MeshRepair.cpp:210

References Slic3r::anonymous_namespace{MeshRepair.cpp}::EdgeUse::a, analyze_topology(), Slic3r::anonymous_namespace{MeshRepair.cpp}::EdgeUse::b, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::bad_face, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::border, Slic3r::anonymous_namespace{MeshRepair.cpp}::EdgeUse::corner, Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::find(), for_each_edge(), NONE, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::open_face, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::open_fraction, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::pinched, same_direction(), sorted_edge_uses(), and Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::unite().

Referenced by analyze_topology(), repair_soup(), and take_closed_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ bbox_contains()

bool Slic3r::anonymous_namespace{MeshRepair.cpp}::bbox_contains ( const CGAL::Bbox_3 & outer,
const CGAL::Bbox_3 & inner,
double tolerance = 0. )
147{
148 return outer.xmin() - tolerance <= inner.xmin() && outer.ymin() - tolerance <= inner.ymin() && outer.zmin() - tolerance <= inner.zmin() &&
149 outer.xmax() + tolerance >= inner.xmax() && outer.ymax() + tolerance >= inner.ymax() && outer.zmax() + tolerance >= inner.zmax();
150}

References bbox_contains().

Referenced by bbox_contains(), fill_holes(), and resolve_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ bbox_diagonal()

double Slic3r::anonymous_namespace{MeshRepair.cpp}::bbox_diagonal ( const std::vector< Point > & points)
163{
164 CGAL::Bbox_3 bb;
165 for (const Point &p : points)
166 bb += p.bbox();
167 return points.empty() ? 0. : std::sqrt(CGAL::square(bb.x_span()) + CGAL::square(bb.y_span()) + CGAL::square(bb.z_span()));
168}
Kernel::Point_3 Point
Definition MeshRepair.cpp:81

References bbox_diagonal().

Referenced by bbox_diagonal(), and repair_soup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clean_soup()

void Slic3r::anonymous_namespace{MeshRepair.cpp}::clean_soup ( std::vector< Point > & points,
std::vector< Triangle > & triangles )
422{
423 std::vector<std::size_t> order(points.size());
424 std::iota(order.begin(), order.end(), std::size_t(0));
425 tbb::parallel_sort(order.begin(), order.end(), [&points](std::size_t a, std::size_t b) {
426 return std::make_tuple(points[a].x(), points[a].y(), points[a].z(), a) < std::make_tuple(points[b].x(), points[b].y(), points[b].z(), b);
427 });
428 std::vector<std::size_t> merged(points.size());
429 for (std::size_t i = 0; i < order.size(); ++i)
430 merged[order[i]] = i > 0 && points[order[i]] == points[order[i - 1]] ? merged[order[i - 1]] : order[i];
431 std::vector<char> degenerate(triangles.size(), 0);
432 for (std::size_t f = 0; f < triangles.size(); ++f) {
433 Triangle &t = triangles[f];
434 for (std::size_t &v : t)
435 v = merged[v];
436 degenerate[f] = t[0] == t[1] || t[1] == t[2] || t[0] == t[2];
437 }
438 erase_marked(triangles, degenerate);
439 remove_duplicate_triangles(points.size(), triangles);
440
441 std::vector<std::size_t> compact(points.size(), NONE);
442 std::vector<Point> used;
443 for (Triangle &t : triangles)
444 for (std::size_t &v : t) {
445 if (compact[v] == NONE) {
446 compact[v] = used.size();
447 used.push_back(points[v]);
448 }
449 v = compact[v];
450 }
451 points = std::move(used);
452}
@ NONE
Definition SideTools.hpp:27
void erase_marked(Container &items, const Mask &marked)
Definition MeshRepair.cpp:118
std::array< std::size_t, 3 > Triangle
Definition MeshRepair.cpp:87
void remove_duplicate_triangles(std::size_t num_points, std::vector< Triangle > &triangles)
Definition MeshRepair.cpp:368
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124

References clean_soup(), erase_marked(), Slic3r::f(), NONE, and remove_duplicate_triangles().

Referenced by clean_soup(), and repair_soup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ close_cracks()

void Slic3r::anonymous_namespace{MeshRepair.cpp}::close_cracks ( Mesh & mesh,
double tolerance )
635{
636 PMP::merge_reversible_connected_components(mesh);
637 PMP::stitch_borders(mesh);
638 if (CGAL::is_closed(mesh))
639 return;
640 // T-junctions, which welding cannot close.
641 PMP::experimental::snap_borders(mesh, CGAL::Constant_property_map<Vertex, double>(tolerance));
642 PMP::stitch_borders(mesh);
643}

References close_cracks().

Referenced by close_cracks(), and repair_unit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ connect_vertices()

UnionFind Slic3r::anonymous_namespace{MeshRepair.cpp}::connect_vertices ( std::size_t num_points,
const std::vector< Triangle > & triangles )
137{
138 UnionFind parts(num_points);
139 for (const Triangle &t : triangles) {
140 parts.unite(t[0], t[1]);
141 parts.unite(t[0], t[2]);
142 }
143 return parts;
144}

References connect_vertices(), and Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::unite().

Referenced by connect_vertices(), remove_duplicate_triangles(), soup_units(), and take_closed_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ erase_marked()

template<class Container, class Mask>
void Slic3r::anonymous_namespace{MeshRepair.cpp}::erase_marked ( Container & items,
const Mask & marked )
119{
120 std::size_t out = 0;
121 for (std::size_t i = 0; i < items.size(); ++i)
122 if (!marked[i])
123 items[out++] = std::move(items[i]);
124 items.resize(out);
125}

References erase_marked().

Referenced by clean_soup(), erase_marked(), remove_duplicate_triangles(), resolve_shells(), and take_closed_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ extract_group()

void Slic3r::anonymous_namespace{MeshRepair.cpp}::extract_group ( const std::vector< Point > & points,
const std::vector< Triangle > & triangles,
const std::vector< std::size_t > & group,
std::vector< Point > & group_points,
std::vector< Triangle > & group_triangles )
268{
269 std::vector<std::size_t> vertices;
270 vertices.reserve(3 * group.size());
271 for (std::size_t f : group)
272 vertices.insert(vertices.end(), triangles[f].begin(), triangles[f].end());
273 std::sort(vertices.begin(), vertices.end());
274 vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end());
275 group_points.clear();
276 for (std::size_t v : vertices)
277 group_points.push_back(points[v]);
278 group_triangles.clear();
279 for (std::size_t f : group) {
280 Triangle &t = group_triangles.emplace_back();
281 for (std::size_t k = 0; k < 3; ++k)
282 t[k] = std::lower_bound(vertices.begin(), vertices.end(), triangles[f][k]) - vertices.begin();
283 }
284}

References extract_group(), and Slic3r::f().

Referenced by extract_group(), and repair_unit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fill_holes()

std::size_t Slic3r::anonymous_namespace{MeshRepair.cpp}::fill_holes ( Mesh & mesh,
double tolerance )
762{
763 std::vector<Halfedge> borders;
764 CGAL::extract_boundary_cycles(mesh, std::back_inserter(borders));
765 std::vector<BorderLoop> loops(borders.size());
766 for (std::size_t i = 0; i < borders.size(); ++i) {
767 BorderLoop &loop = loops[i];
768 loop.halfedge = borders[i];
769 Vector newell(0, 0, 0);
770 for (Halfedge h : CGAL::halfedges_around_face(borders[i], mesh)) {
771 loop.halfedges.push_back(h);
772 newell = newell + CGAL::cross_product(mesh.point(mesh.source(h)) - CGAL::ORIGIN, mesh.point(mesh.target(h)) - CGAL::ORIGIN);
773 loop.bbox += mesh.point(mesh.source(h)).bbox();
774 }
775 if (const double len = std::sqrt(newell.squared_length()); len > 0.) {
776 loop.normal = newell / len;
777 loop.offset = (mesh.point(mesh.source(loop.halfedge)) - CGAL::ORIGIN) * loop.normal;
778 loop.planar = std::all_of(loop.halfedges.begin(), loop.halfedges.end(), [&](Halfedge h) {
779 return std::abs((mesh.point(mesh.source(h)) - CGAL::ORIGIN) * loop.normal - loop.offset) <= tolerance;
780 });
781 }
782 }
783
784 // Planar loops facing each other in the same plane, one inside the other, bound one region.
785 UnionFind regions(loops.size());
786 std::vector<char> grouped(loops.size(), 0);
787 for (std::size_t a = 0; a < loops.size(); ++a)
788 for (std::size_t b = 0; b < loops.size(); ++b) {
789 const BorderLoop &la = loops[a], &lb = loops[b];
790 if (a == b || !la.planar || !lb.planar || la.normal * lb.normal > -0.99 || !bbox_contains(la.bbox, lb.bbox, tolerance))
791 continue;
792 if (std::all_of(lb.halfedges.begin(), lb.halfedges.end(), [&](Halfedge h) {
793 return std::abs((mesh.point(mesh.source(h)) - CGAL::ORIGIN) * la.normal - la.offset) <= tolerance;
794 })) {
795 regions.unite(a, b);
796 grouped[a] = grouped[b] = 1;
797 }
798 }
799 std::map<std::size_t, std::vector<std::size_t>> groups;
800 for (std::size_t i = 0; i < loops.size(); ++i)
801 if (grouped[i])
802 groups[regions.find(i)].push_back(i);
803
804 std::size_t filled = 0;
805 std::vector<char> done(loops.size(), 0);
806 for (auto &[root, members] : groups) {
807 // The outermost loop sets the orientation of the region.
808 auto extent = [&loops](std::size_t i) { return loops[i].bbox.x_span() + loops[i].bbox.y_span() + loops[i].bbox.z_span(); };
809 std::sort(members.begin(), members.end(), [&extent](std::size_t l, std::size_t r) { return extent(l) > extent(r); });
810 std::vector<const BorderLoop *> group;
811 for (std::size_t i : members)
812 group.push_back(&loops[i]);
813 if (fill_planar_region(mesh, group)) {
814 filled += members.size();
815 for (std::size_t i : members)
816 done[i] = 1;
817 }
818 }
819
820 for (std::size_t i = 0; i < loops.size(); ++i) {
821 if (done[i])
822 continue;
823 ++filled;
824 std::vector<Face> patch;
825 // Searching all triangles is cubic in the loop length; the search among Delaunay ones varies from run to run.
826 const std::size_t size = loops[i].halfedges.size();
827 PMP::triangulate_hole(mesh, loops[i].halfedge,
828 CGAL::parameters::use_2d_constrained_delaunay_triangulation(true)
829 .use_delaunay_triangulation(size > 100)
830 .do_not_use_cubic_algorithm(size > 200)
831 .face_output_iterator(std::back_inserter(patch)));
832 if (patch.empty() && CGAL::is_border(loops[i].halfedge, mesh))
833 fill_with_fan(mesh, loops[i]);
834 }
835 return filled;
836}
Kernel::Vector_3 Vector
Definition MeshRepair.cpp:82
Mesh::Halfedge_index Halfedge
Definition MeshRepair.cpp:85
double offset
Definition MeshRepair.cpp:650
CGAL::Bbox_3 bbox
Definition MeshRepair.cpp:652
Halfedge halfedge
Definition MeshRepair.cpp:647
bool fill_planar_region(Mesh &mesh, const std::vector< const BorderLoop * > &loops)
Definition MeshRepair.cpp:693
std::vector< Halfedge > halfedges
Definition MeshRepair.cpp:648
void fill_with_fan(Mesh &mesh, const BorderLoop &loop)
Definition MeshRepair.cpp:751
Vector normal
Definition MeshRepair.cpp:649
bool bbox_contains(const CGAL::Bbox_3 &outer, const CGAL::Bbox_3 &inner, double tolerance=0.)
Definition MeshRepair.cpp:146

References Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop::bbox, bbox_contains(), fill_holes(), fill_planar_region(), fill_with_fan(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::find(), Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop::halfedge, Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop::halfedges, Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop::normal, Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop::offset, Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop::planar, and Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::unite().

Referenced by fill_holes(), and repair_unit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fill_planar_region()

bool Slic3r::anonymous_namespace{MeshRepair.cpp}::fill_planar_region ( Mesh & mesh,
const std::vector< const BorderLoop * > & loops )
694{
695 const Vector &n = loops.front()->normal;
696 const Vector u = std::abs(n.x()) < 0.9 ? CGAL::cross_product(n, Vector(1, 0, 0)) : CGAL::cross_product(n, Vector(0, 1, 0));
697 const Vector e1 = u / std::sqrt(u.squared_length());
698 const Vector e2 = CGAL::cross_product(n, e1);
699
700 Cdt cdt;
701 std::size_t num_vertices = 0;
702 std::set<std::pair<Vertex, Vertex>> border_edges;
703 try {
704 for (const BorderLoop *loop : loops) {
705 std::vector<Cdt::Vertex_handle> vh;
706 for (Halfedge h : loop->halfedges) {
707 const Vector p = mesh.point(mesh.source(h)) - CGAL::ORIGIN;
708 vh.push_back(cdt.insert(Kernel::Point_2(p * e1, p * e2)));
709 vh.back()->info() = mesh.source(h);
710 border_edges.emplace(mesh.source(h), mesh.target(h));
711 }
712 num_vertices += vh.size();
713 for (std::size_t k = 0; k < vh.size(); ++k)
714 cdt.insert_constraint(vh[k], vh[(k + 1) % vh.size()]);
715 }
716 } catch (const std::exception &) {
717 return false;
718 }
719 // Loop vertices projecting onto the same point were merged by the triangulation.
720 if (cdt.number_of_vertices() != num_vertices)
721 return false;
722 mark_nesting(cdt);
723
724 std::vector<std::array<Vertex, 3>> patch;
725 std::set<std::pair<Vertex, Vertex>> patch_edges;
726 for (Cdt::Face_handle f : cdt.finite_face_handles())
727 if (f->info().nesting % 2 == 1) {
728 const std::array<Vertex, 3> &t = patch.emplace_back(std::array<Vertex, 3>{f->vertex(0)->info(), f->vertex(1)->info(), f->vertex(2)->info()});
729 for (int k = 0; k < 3; ++k)
730 patch_edges.emplace(t[k], t[(k + 1) % 3]);
731 }
732 // The patch has to close every loop with the orientation of the triangles around it.
733 for (const auto &e : border_edges)
734 if (patch_edges.count(e) == 0)
735 return false;
736
737 std::vector<Face> added;
738 for (const auto &t : patch) {
739 const Face f = mesh.add_face(t[0], t[1], t[2]);
740 if (f == Mesh::null_face()) {
741 for (Face a : added)
742 mesh.remove_face(a);
743 return false;
744 }
745 added.push_back(f);
746 }
747 return true;
748}
Definition VoronoiUtilsCgal.cpp:27
CGAL::Constrained_Delaunay_triangulation_2< Kernel, CGAL::Triangulation_data_structure_2< CdtVb, CdtFb >, CGAL::No_constraint_intersection_tag > Cdt
Definition MeshRepair.cpp:661
void mark_nesting(Cdt &cdt)
Definition MeshRepair.cpp:664
Mesh::Face_index Face
Definition MeshRepair.cpp:86

References Slic3r::f(), fill_planar_region(), and mark_nesting().

Referenced by fill_holes(), and fill_planar_region().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fill_with_fan()

void Slic3r::anonymous_namespace{MeshRepair.cpp}::fill_with_fan ( Mesh & mesh,
const BorderLoop & loop )
752{
753 Vector sum(0, 0, 0);
754 for (Halfedge h : loop.halfedges)
755 sum = sum + (mesh.point(mesh.source(h)) - CGAL::ORIGIN);
756 const Vertex c = mesh.add_vertex(CGAL::ORIGIN + sum / double(loop.halfedges.size()));
757 for (Halfedge h : loop.halfedges)
758 mesh.add_face(mesh.source(h), mesh.target(h), c);
759}
Mesh::Vertex_index Vertex
Definition MeshRepair.cpp:84

References fill_with_fan(), and Slic3r::anonymous_namespace{MeshRepair.cpp}::BorderLoop::halfedges.

Referenced by fill_holes(), and fill_with_fan().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ for_each_edge()

template<class Fn>
void Slic3r::anonymous_namespace{MeshRepair.cpp}::for_each_edge ( const std::vector< EdgeUse > & uses,
Fn && fn )
192{
193 for (std::size_t i = 0; i < uses.size();) {
194 std::size_t j = i + 1;
195 while (j < uses.size() && uses[j].a == uses[i].a && uses[j].b == uses[i].b)
196 ++j;
197 fn(i, j);
198 i = j;
199 }
200}

References for_each_edge().

Referenced by analyze_topology(), for_each_edge(), and orient_soup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ inner_sample()

Point Slic3r::anonymous_namespace{MeshRepair.cpp}::inner_sample ( const Mesh & shell)
887{
888 Face best;
889 double best_area = -1.;
890 for (Face f : shell.faces())
891 if (const double a = PMP::face_area(f, shell); a > best_area) {
892 best_area = a;
893 best = f;
894 }
895 const Halfedge h = shell.halfedge(best);
896 return CGAL::centroid(shell.point(shell.source(h)), shell.point(shell.target(h)), shell.point(shell.target(shell.next(h))));
897}

References Slic3r::f(), and inner_sample().

Referenced by inner_sample(), and resolve_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ keep_input_orientation()

std::size_t Slic3r::anonymous_namespace{MeshRepair.cpp}::keep_input_orientation ( Mesh & mesh,
const std::vector< Point > & points_before,
const std::vector< Triangle > & triangles_before,
const std::vector< Point > & points,
const std::vector< Triangle > & triangles )
606{
607 if (mesh.number_of_faces() != triangles.size())
608 return 0;
609 auto fcc = mesh.add_property_map<Face, std::size_t>("f:repair_part").first;
610 const std::size_t num_parts = PMP::connected_components(mesh, fcc);
611 std::vector<double> vote(num_parts, 0.);
612 std::size_t i = 0;
613 for (Face f : mesh.faces()) {
614 const Triangle &a = triangles_before[i];
615 const Triangle &b = triangles[i++];
616 bool same = false;
617 for (std::size_t r = 0; r < 3 && !same; ++r) {
618 same = true;
619 for (std::size_t k = 0; k < 3 && same; ++k)
620 same = points_before[a[k]] == points[b[(k + r) % 3]];
621 }
622 vote[fcc[f]] += same ? PMP::face_area(f, mesh) : -PMP::face_area(f, mesh);
623 }
624 std::vector<Face> to_reverse;
625 for (Face f : mesh.faces())
626 if (vote[fcc[f]] < 0.)
627 to_reverse.push_back(f);
628 mesh.remove_property_map(fcc);
629 if (!to_reverse.empty())
630 PMP::reverse_face_orientations(to_reverse, mesh);
631 return std::count_if(vote.begin(), vote.end(), [](double v) { return v < 0.; });
632}

References Slic3r::f(), and keep_input_orientation().

Referenced by keep_input_orientation(), and repair_unit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ mark_nesting()

void Slic3r::anonymous_namespace{MeshRepair.cpp}::mark_nesting ( Cdt & cdt)
665{
666 std::vector<std::pair<Cdt::Face_handle, int>> seeds{{cdt.infinite_face(), 0}};
667 while (!seeds.empty()) {
668 auto [seed, level] = seeds.back();
669 seeds.pop_back();
670 if (seed->info().nesting != -1)
671 continue;
672 seed->info().nesting = level;
673 std::vector<Cdt::Face_handle> queue{seed};
674 while (!queue.empty()) {
675 Cdt::Face_handle f = queue.back();
676 queue.pop_back();
677 for (int i = 0; i < 3; ++i) {
678 Cdt::Face_handle n = f->neighbor(i);
679 if (n->info().nesting != -1)
680 continue;
681 if (cdt.is_constrained({f, i}))
682 seeds.emplace_back(n, level + 1);
683 else {
684 n->info().nesting = level;
685 queue.push_back(n);
686 }
687 }
688 }
689 }
690}

References Slic3r::f(), and mark_nesting().

Referenced by fill_planar_region(), and mark_nesting().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ orient_soup()

bool Slic3r::anonymous_namespace{MeshRepair.cpp}::orient_soup ( std::vector< Point > & points,
std::vector< Triangle > & triangles,
std::size_t & reversed_parts )
500{
501 const std::size_t num_faces = triangles.size();
502 const std::vector<EdgeUse> uses = sorted_edge_uses(triangles);
503 std::vector<std::size_t> twin(3 * num_faces, NONE);
504 std::vector<std::pair<std::size_t, std::size_t>> cut; // edges used by more than two triangles
505 for_each_edge(uses, [&](std::size_t i, std::size_t j) {
506 if (j - i == 2) {
507 twin[uses[i].corner] = uses[i + 1].corner;
508 twin[uses[i + 1].corner] = uses[i].corner;
509 } else if (j - i > 2)
510 cut.emplace_back(i, j);
511 });
512
513 std::vector<signed char> flip(num_faces, -1);
514 std::vector<std::size_t> part_of(num_faces);
515 std::vector<double> vote;
516 std::vector<char> conflict(3 * num_faces, 0);
517 std::vector<std::size_t> stack;
518 for (std::size_t seed = 0; seed < num_faces; ++seed) {
519 if (flip[seed] >= 0)
520 continue;
521 const std::size_t part = vote.size();
522 vote.push_back(0.);
523 flip[seed] = 0;
524 stack.push_back(seed);
525 while (!stack.empty()) {
526 const std::size_t f = stack.back();
527 stack.pop_back();
528 part_of[f] = part;
529 const Triangle &t = triangles[f];
530 const double area = std::sqrt(CGAL::squared_area(points[t[0]], points[t[1]], points[t[2]]));
531 vote[part] += flip[f] ? -area : area;
532 for (std::size_t k = 0; k < 3; ++k) {
533 const std::size_t c = 3 * f + k, n = twin[c];
534 if (n == NONE)
535 continue;
536 const std::size_t g = n / 3;
537 // Triangles agree when they run along their shared edge in opposite directions.
538 const signed char want = flip[f] ^ (t[k] == triangles[g][n % 3]);
539 if (flip[g] < 0) {
540 flip[g] = want;
541 stack.push_back(g);
542 } else if (flip[g] != want)
543 conflict[c] = conflict[n] = 1;
544 }
545 }
546 }
547
548 // Corners keep their input vertex in original, through the flips below.
549 std::vector<std::size_t> original(3 * num_faces);
550 std::vector<char> part_reversed(vote.size(), 0);
551 for (std::size_t f = 0; f < num_faces; ++f) {
552 if (flip[f] != (vote[part_of[f]] < 0.)) {
553 std::swap(triangles[f][1], triangles[f][2]);
554 part_reversed[part_of[f]] = 1;
555 }
556 for (std::size_t k = 0; k < 3; ++k)
557 original[3 * f + k] = triangles[f][k];
558 }
559 reversed_parts = std::count(part_reversed.begin(), part_reversed.end(), 1);
560
561 auto corner_of = [&original](std::size_t f, std::size_t v) { return 3 * f + (original[3 * f] == v ? 0 : original[3 * f + 1] == v ? 1 : 2); };
562 UnionFind fans(3 * num_faces);
563 for (const EdgeUse &e : uses)
564 if (const std::size_t n = twin[e.corner]; n != NONE && e.corner < n && !conflict[e.corner]) {
565 fans.unite(corner_of(e.corner / 3, e.a), corner_of(n / 3, e.a));
566 fans.unite(corner_of(e.corner / 3, e.b), corner_of(n / 3, e.b));
567 }
568 std::vector<std::size_t> fan_vertex(3 * num_faces, NONE);
569 std::vector<char> used(points.size(), 0);
570 for (std::size_t c = 0; c < 3 * num_faces; ++c) {
571 std::size_t &copy = fan_vertex[fans.find(c)];
572 if (copy == NONE) {
573 const std::size_t v = original[c];
574 copy = used[v] ? points.size() : v;
575 if (used[v])
576 points.push_back(points[v]);
577 used[v] = 1;
578 }
579 triangles[c / 3][c % 3] = copy;
580 }
581
582 // Only edges that were cut can still be shared wrongly: by more than two triangles, or twice in one direction.
583 for (std::size_t k = 0; k < uses.size(); ++k)
584 if (conflict[uses[k].corner] && uses[k].corner < twin[uses[k].corner])
585 cut.emplace_back(k, k + 2);
586 for (const auto &[i, j] : cut) {
587 // Each use as its copied endpoints and whether it runs from a to b.
588 std::vector<std::array<std::size_t, 3>> copies;
589 for (std::size_t k = i; k < j; ++k) {
590 const std::size_t f = uses[k].corner / 3, ka = corner_of(f, uses[k].a) % 3, kb = corner_of(f, uses[k].b) % 3;
591 const auto [ca, cb] = std::minmax(triangles[f][ka], triangles[f][kb]);
592 copies.push_back({ca, cb, std::size_t((ka + 1) % 3 == kb)});
593 }
594 std::sort(copies.begin(), copies.end());
595 for (std::size_t k = 0; k + 1 < copies.size(); ++k)
596 if (copies[k][0] == copies[k + 1][0] && copies[k][1] == copies[k + 1][1] &&
597 (copies[k][2] == copies[k + 1][2] || (k + 2 < copies.size() && copies[k + 2][0] == copies[k][0] && copies[k + 2][1] == copies[k][1])))
598 return false;
599 }
600 return true;
601}
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511

References Slic3r::area(), Slic3r::f(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::find(), for_each_edge(), NONE, orient_soup(), sorted_edge_uses(), and Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::unite().

Referenced by orient_soup(), and repair_unit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ orient_with_cgal()

void Slic3r::anonymous_namespace{MeshRepair.cpp}::orient_with_cgal ( std::vector< Point > & points,
std::vector< Triangle > & triangles )
256{
257 std::vector<std::vector<std::size_t>> polygons(triangles.size());
258 for (std::size_t i = 0; i < triangles.size(); ++i)
259 polygons[i].assign(triangles[i].begin(), triangles[i].end());
260 PMP::orient_polygon_soup(points, polygons);
261 for (std::size_t i = 0; i < triangles.size(); ++i)
262 std::copy(polygons[i].begin(), polygons[i].end(), triangles[i].begin());
263}

References orient_with_cgal().

Referenced by orient_with_cgal(), and repair_unit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ overlapping_boxes()

std::vector< std::pair< std::size_t, std::size_t > > Slic3r::anonymous_namespace{MeshRepair.cpp}::overlapping_boxes ( std::vector< Box > & boxes)
153{
154 std::vector<std::pair<std::size_t, std::size_t>> pairs;
155 CGAL::box_self_intersection_d(boxes.begin(), boxes.end(), [&pairs](const Box &a, const Box &b) {
156 pairs.emplace_back(std::min(a.info(), b.info()), std::max(a.info(), b.info()));
157 }, std::ptrdiff_t(10), CGAL::Box_intersection_d::CLOSED);
158 std::sort(pairs.begin(), pairs.end());
159 return pairs;
160}
CGAL::Box_intersection_d::Box_with_info_d< double, 3, std::size_t > Box
Definition MeshRepair.cpp:89

References overlapping_boxes().

Referenced by overlapping_boxes(), resolve_shells(), and soup_units().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parallel_for_each()

template<class Fn>
void Slic3r::anonymous_namespace{MeshRepair.cpp}::parallel_for_each ( std::size_t n,
Fn && fn )
128{
129 tbb::parallel_for(tbb::blocked_range<std::size_t>(0, n, 1), [&fn](const tbb::blocked_range<std::size_t> &range) {
130 for (std::size_t i = range.begin(); i < range.end(); ++i)
131 fn(i);
132 });
133}
auto range(Cont &&cont)
Definition libslic3r.h:405

References parallel_for_each(), and Slic3r::range().

Referenced by parallel_for_each(), repair_soup(), resolve_shells(), and soup_to_meshes().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ remove_duplicate_triangles()

void Slic3r::anonymous_namespace{MeshRepair.cpp}::remove_duplicate_triangles ( std::size_t num_points,
std::vector< Triangle > & triangles )
369{
370 struct Entry
371 {
372 std::array<std::size_t, 3> key;
373 bool odd;
374 std::size_t idx;
375 };
376 std::vector<Entry> entries;
377 for (std::size_t i = 0; i < triangles.size(); ++i) {
378 const Triangle &t = triangles[i];
379 const std::size_t r = std::min_element(t.begin(), t.end()) - t.begin();
380 const std::size_t b = t[(r + 1) % 3], c = t[(r + 2) % 3];
381 entries.push_back({{t[r], std::min(b, c), std::max(b, c)}, b > c, i});
382 }
383 tbb::parallel_sort(entries.begin(), entries.end(), [](const Entry &l, const Entry &r) { return std::tie(l.key, l.idx) < std::tie(r.key, r.idx); });
384
385 std::vector<char> remove(triangles.size(), 0);
386 std::vector<std::size_t> cancelled;
387 for (std::size_t i = 0; i < entries.size();) {
388 std::size_t j = i + 1;
389 while (j < entries.size() && entries[j].key == entries[i].key)
390 ++j;
391 if (j - i > 1) {
392 std::size_t num_odd = 0;
393 for (std::size_t k = i; k < j; ++k)
394 num_odd += entries[k].odd;
395 const bool odd_wins = 2 * num_odd > j - i;
396 bool kept = 2 * num_odd == j - i;
397 if (kept)
398 cancelled.push_back(entries[i].idx);
399 for (std::size_t k = i; k < j; ++k) {
400 if (!kept && entries[k].odd == odd_wins)
401 kept = true;
402 else
403 remove[entries[k].idx] = 1;
404 }
405 }
406 i = j;
407 }
408 if (!cancelled.empty()) {
409 UnionFind regions = connect_vertices(num_points, triangles);
410 std::vector<char> survives(num_points, 0);
411 for (std::size_t i = 0; i < triangles.size(); ++i)
412 if (!remove[i])
413 survives[regions.find(triangles[i][0])] = 1;
414 for (std::size_t i : cancelled)
415 if (!survives[regions.find(triangles[i][0])])
416 remove[i] = 0;
417 }
418 erase_marked(triangles, remove);
419}
UnionFind connect_vertices(std::size_t num_points, const std::vector< Triangle > &triangles)
Definition MeshRepair.cpp:136

References connect_vertices(), erase_marked(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::find(), and remove_duplicate_triangles().

Referenced by clean_soup(), and remove_duplicate_triangles().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ remove_local_self_intersections()

std::size_t Slic3r::anonymous_namespace{MeshRepair.cpp}::remove_local_self_intersections ( Mesh & shell)
851{
852 std::vector<std::pair<Face, Face>> pairs;
853 PMP::self_intersections<CGAL::Parallel_if_available_tag>(shell, std::back_inserter(pairs));
854 if (pairs.empty())
855 return 0;
856 std::set<Face> faces;
857 for (const auto &[a, b] : pairs) {
858 faces.insert(a);
859 faces.insert(b);
860 }
861 if (20 * faces.size() > shell.number_of_faces())
862 return 0;
863 const double volume = signed_volume(shell);
864 Mesh backup = shell;
865 try {
866 PMP::experimental::remove_self_intersections(faces, shell, CGAL::parameters::preserve_genus(false));
867 shell.collect_garbage();
868 if (CGAL::is_valid_polygon_mesh(shell) && CGAL::is_closed(shell) && std::abs(signed_volume(shell) - volume) <= 1e-3 * std::abs(volume))
869 return pairs.size();
870 } catch (const std::exception &) {
871 }
872 shell = std::move(backup);
873 return 0;
874}
double signed_volume(const Mesh &mesh)
Definition MeshRepair.cpp:838
CGAL::Surface_mesh< Point > Mesh
Definition MeshRepair.cpp:83

References remove_local_self_intersections(), and signed_volume().

Referenced by remove_local_self_intersections().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ repair_its()

bool Slic3r::anonymous_namespace{MeshRepair.cpp}::repair_its ( const indexed_triangle_set & its,
indexed_triangle_set & result,
MeshRepairStats & stats,
const std::function< void(int)> & progress )
1150{
1151 std::vector<Point> points;
1152 points.reserve(its.vertices.size());
1153 for (const stl_vertex &v : its.vertices)
1154 points.emplace_back(v.x(), v.y(), v.z());
1155 std::vector<Triangle> triangles;
1156 triangles.reserve(its.indices.size());
1157 for (const stl_triangle_vertex_indices &f : its.indices)
1158 if (its.vertices[f[0]].allFinite() && its.vertices[f[1]].allFinite() && its.vertices[f[2]].allFinite())
1159 triangles.push_back({std::size_t(f[0]), std::size_t(f[1]), std::size_t(f[2])});
1160 const bool dropped = triangles.size() != its.indices.size();
1161
1162 std::vector<Mesh> shells;
1163 const bool ok = repair_soup(points, triangles, shells, stats, progress);
1164 stats.modified = stats.modified || dropped;
1165 if (!ok || !stats.modified)
1166 return ok;
1167
1168 for (Mesh &shell : shells) {
1169 shell.collect_garbage();
1170 const int offset = int(result.vertices.size());
1171 for (Vertex v : shell.vertices()) {
1172 const Point &p = shell.point(v);
1173 result.vertices.emplace_back(float(p.x()), float(p.y()), float(p.z()));
1174 }
1175 for (Face f : shell.faces()) {
1176 const Halfedge h = shell.halfedge(f);
1177 result.indices.emplace_back(offset + int(shell.source(h)), offset + int(shell.target(h)), offset + int(shell.target(shell.next(h))));
1178 }
1179 }
1180 return true;
1181}
Definition Point.hpp:199
bool repair_soup(std::vector< Point > &points, std::vector< Triangle > &triangles, std::vector< Mesh > &shells, MeshRepairStats &stats, const std::function< void(int)> &progress)
Definition MeshRepair.cpp:1077
bool modified
Definition MeshRepair.hpp:20
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582

References Slic3r::f(), Slic3r::MeshRepairStats::modified, Slic3r::offset(), repair_its(), and repair_soup().

Referenced by repair_its().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ repair_soup()

bool Slic3r::anonymous_namespace{MeshRepair.cpp}::repair_soup ( std::vector< Point > & points,
std::vector< Triangle > & triangles,
std::vector< Mesh > & shells,
MeshRepairStats & stats,
const std::function< void(int)> & progress )
1079{
1080 const double tolerance = 1e-4 * bbox_diagonal(points);
1081 shells = take_closed_shells(points, triangles);
1082 stats.modified = !triangles.empty();
1083 progress(10);
1084
1085 if (!triangles.empty()) {
1086 const std::size_t num_triangles = triangles.size();
1087 clean_soup(points, triangles);
1088 SoupTopology topology = analyze_topology(points.size(), triangles);
1089 // A soup that stays mostly open has cracks wider than the tolerance, so try wider ones.
1090 for (double t = tolerance; t > 0. && t <= 100. * tolerance; t *= 10.) {
1091 if (const std::size_t welded = weld_border_vertices(points, triangles, topology.border, t); welded > 0) {
1092 stats.welded_vertices += welded;
1093 clean_soup(points, triangles);
1094 topology = analyze_topology(points.size(), triangles);
1095 }
1096 if (topology.open_fraction < 0.5)
1097 break;
1098 }
1099 stats.removed_triangles = num_triangles - triangles.size();
1100 progress(25);
1101
1102 // Independent parts of the soup are repaired in parallel.
1103 const std::vector<std::vector<std::size_t>> units = soup_units(points, triangles, topology, tolerance);
1104 std::vector<UnitResult> results(units.size());
1105 parallel_for_each(units.size(), [&](std::size_t i) { results[i] = repair_unit(points, triangles, units[i], topology, tolerance); });
1106 for (UnitResult &result : results) {
1107 if (!result.closed)
1108 return false;
1109 stats.reversed_shells += result.reversed;
1110 stats.filled_holes += result.filled;
1111 for (Mesh &shell : result.shells)
1112 shells.emplace_back(std::move(shell));
1113 }
1114 progress(60);
1115 }
1116
1117 // Shells are independent from here on. Hole filling and remeshing leave some that enclose nothing.
1118 auto flat = [tolerance](const Mesh &shell) {
1119 // Fewer than four faces are two triangles back to back, whose volumes need not cancel exactly.
1120 return shell.number_of_faces() < 4 || std::abs(signed_volume(shell)) <= 0.1 * tolerance * PMP::area(shell);
1121 };
1122 std::vector<std::vector<Mesh>> pieces(shells.size());
1123 std::vector<std::size_t> pairs(shells.size(), 0), removed(shells.size(), 0);
1124 parallel_for_each(shells.size(), [&](std::size_t i) {
1125 if (flat(shells[i]))
1126 removed[i] = 1;
1127 else if ((pairs[i] = remove_local_self_intersections(shells[i])) == 0)
1128 pieces[i].emplace_back(std::move(shells[i]));
1129 else
1130 for (Mesh &piece : split_shells(shells[i]))
1131 if (flat(piece))
1132 ++removed[i];
1133 else
1134 pieces[i].emplace_back(std::move(piece));
1135 });
1136 stats.removed_shells += std::accumulate(removed.begin(), removed.end(), std::size_t(0));
1137 stats.self_intersections += std::accumulate(pairs.begin(), pairs.end(), std::size_t(0));
1138 shells.clear();
1139 for (std::vector<Mesh> &shell_pieces : pieces)
1140 std::move(shell_pieces.begin(), shell_pieces.end(), std::back_inserter(shells));
1141 progress(90);
1142 resolve_shells(shells, stats, progress);
1143
1144 stats.modified = stats.modified || stats.removed_shells > 0 || stats.reversed_shells > 0 || stats.self_intersections > 0;
1145 return !shells.empty();
1146}
std::vector< std::vector< std::size_t > > soup_units(const std::vector< Point > &points, const std::vector< Triangle > &triangles, const SoupTopology &topology, double tolerance)
Definition MeshRepair.cpp:975
double bbox_diagonal(const std::vector< Point > &points)
Definition MeshRepair.cpp:162
std::vector< Mesh > take_closed_shells(const std::vector< Point > &points, std::vector< Triangle > &triangles)
Definition MeshRepair.cpp:340
void parallel_for_each(std::size_t n, Fn &&fn)
Definition MeshRepair.cpp:127
std::size_t weld_border_vertices(const std::vector< Point > &points, std::vector< Triangle > &triangles, const std::vector< std::size_t > &border, double tolerance)
Definition MeshRepair.cpp:455
void clean_soup(std::vector< Point > &points, std::vector< Triangle > &triangles)
Definition MeshRepair.cpp:421
void resolve_shells(std::vector< Mesh > &shells, MeshRepairStats &stats, const std::function< void(int)> &progress)
Definition MeshRepair.cpp:900
SoupTopology analyze_topology(std::size_t num_points, const std::vector< Triangle > &triangles)
Definition MeshRepair.cpp:217
std::size_t self_intersections
Definition MeshRepair.hpp:18
std::size_t filled_holes
Definition MeshRepair.hpp:16
std::size_t removed_triangles
Definition MeshRepair.hpp:14
std::size_t reversed_shells
Definition MeshRepair.hpp:15
std::size_t removed_shells
Definition MeshRepair.hpp:17
std::size_t welded_vertices
Definition MeshRepair.hpp:13

References analyze_topology(), bbox_diagonal(), Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::border, clean_soup(), Slic3r::MeshRepairStats::filled_holes, Slic3r::MeshRepairStats::modified, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::open_fraction, parallel_for_each(), Slic3r::MeshRepairStats::removed_shells, Slic3r::MeshRepairStats::removed_triangles, repair_soup(), resolve_shells(), Slic3r::MeshRepairStats::reversed_shells, Slic3r::MeshRepairStats::self_intersections, signed_volume(), soup_units(), take_closed_shells(), weld_border_vertices(), and Slic3r::MeshRepairStats::welded_vertices.

Referenced by repair_its(), and repair_soup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ repair_unit()

UnitResult Slic3r::anonymous_namespace{MeshRepair.cpp}::repair_unit ( const std::vector< Point > & points,
const std::vector< Triangle > & triangles,
const std::vector< std::size_t > & faces,
const SoupTopology & topology,
double tolerance )
1030{
1031 std::vector<Point> unit_points;
1032 std::vector<Triangle> unit_triangles;
1033 extract_group(points, triangles, faces, unit_points, unit_triangles);
1034 bool oriented_manifold = true;
1035 for (std::size_t f : faces) {
1036 oriented_manifold = oriented_manifold && !topology.bad_face[f];
1037 for (std::size_t v : triangles[f])
1038 oriented_manifold = oriented_manifold && !topology.pinched[v];
1039 }
1040
1041 UnitResult result;
1042 Mesh mesh;
1043 if (oriented_manifold)
1044 PMP::polygon_soup_to_polygon_mesh(unit_points, unit_triangles, mesh);
1045 else {
1046 std::vector<Point> oriented_points = unit_points;
1047 std::vector<Triangle> oriented_triangles = unit_triangles;
1048 if (orient_soup(oriented_points, oriented_triangles, result.reversed))
1049 PMP::polygon_soup_to_polygon_mesh(oriented_points, oriented_triangles, mesh);
1050 else {
1051 const std::vector<Point> points_before = unit_points;
1052 const std::vector<Triangle> triangles_before = unit_triangles;
1053 orient_with_cgal(unit_points, unit_triangles);
1054 PMP::polygon_soup_to_polygon_mesh(unit_points, unit_triangles, mesh);
1055 result.reversed = keep_input_orientation(mesh, points_before, triangles_before, unit_points, unit_triangles);
1056 }
1057 }
1058
1059 if (!CGAL::is_closed(mesh))
1060 close_cracks(mesh, tolerance);
1061 PMP::remove_degenerate_faces(mesh);
1062 if (!CGAL::is_closed(mesh)) {
1063 // Rejoin vertices the orientation split where holes break their fan, so each gap is one loop.
1064 PMP::merge_duplicated_vertices_in_boundary_cycles(mesh);
1065 result.filled = fill_holes(mesh, tolerance);
1066 PMP::remove_degenerate_faces(mesh);
1067 }
1068 PMP::remove_isolated_vertices(mesh);
1069 mesh.collect_garbage();
1070 result.closed = CGAL::is_closed(mesh);
1071 if (result.closed && !mesh.is_empty())
1072 result.shells = split_shells(mesh);
1073 return result;
1074}
void extract_group(const std::vector< Point > &points, const std::vector< Triangle > &triangles, const std::vector< std::size_t > &group, std::vector< Point > &group_points, std::vector< Triangle > &group_triangles)
Definition MeshRepair.cpp:266
std::size_t keep_input_orientation(Mesh &mesh, const std::vector< Point > &points_before, const std::vector< Triangle > &triangles_before, const std::vector< Point > &points, const std::vector< Triangle > &triangles)
Definition MeshRepair.cpp:604
void close_cracks(Mesh &mesh, double tolerance)
Definition MeshRepair.cpp:634
std::size_t fill_holes(Mesh &mesh, double tolerance)
Definition MeshRepair.cpp:761
std::vector< Mesh > split_shells(Mesh &mesh)
Definition MeshRepair.cpp:302
bool orient_soup(std::vector< Point > &points, std::vector< Triangle > &triangles, std::size_t &reversed_parts)
Definition MeshRepair.cpp:499
void orient_with_cgal(std::vector< Point > &points, std::vector< Triangle > &triangles)
Definition MeshRepair.cpp:255

References Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::bad_face, close_cracks(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnitResult::closed, extract_group(), Slic3r::f(), fill_holes(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnitResult::filled, keep_input_orientation(), orient_soup(), orient_with_cgal(), Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::pinched, repair_unit(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnitResult::reversed, Slic3r::anonymous_namespace{MeshRepair.cpp}::UnitResult::shells, and split_shells().

Referenced by repair_unit().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ resolve_shells()

void Slic3r::anonymous_namespace{MeshRepair.cpp}::resolve_shells ( std::vector< Mesh > & shells,
MeshRepairStats & stats,
const std::function< void(int)> & progress )
901{
902 const std::size_t n = shells.size();
903 std::vector<CGAL::Bbox_3> bbox(n);
904 std::vector<int> sign(n);
905 std::vector<Box> boxes;
906 for (std::size_t i = 0; i < n; ++i) {
907 bbox[i] = PMP::bbox(shells[i]);
908 sign[i] = signed_volume(shells[i]) < 0. ? -1 : 1;
909 boxes.emplace_back(bbox[i], i);
910 }
911 const std::vector<std::pair<std::size_t, std::size_t>> overlaps = overlapping_boxes(boxes);
912
913 std::vector<std::vector<std::size_t>> enclosing(n);
914 std::vector<char> needs_tree(n, 0);
915 for (const auto &[a, b] : overlaps) {
916 needs_tree[shells[a].number_of_faces() < shells[b].number_of_faces() ? b : a] = 1;
917 if (bbox_contains(bbox[a], bbox[b]))
918 enclosing[b].push_back(a);
919 if (bbox_contains(bbox[b], bbox[a]))
920 enclosing[a].push_back(b);
921 }
922 for (const std::vector<std::size_t> &outer : enclosing)
923 for (std::size_t k : outer)
924 needs_tree[k] = 1;
925 // Built before any query: an AABB tree finishes its construction lazily on the first one.
926 std::vector<std::unique_ptr<Tree>> trees(n);
927 parallel_for_each(n, [&](std::size_t i) {
928 if (needs_tree[i]) {
929 trees[i] = std::make_unique<Tree>(shells[i].faces().begin(), shells[i].faces().end(), shells[i]);
930 trees[i]->build();
931 }
932 });
933
934 std::vector<char> hit(overlaps.size(), 0);
935 parallel_for_each(overlaps.size(), [&](std::size_t i) {
936 const auto [a, b] = overlaps[i];
937 const bool a_smaller = shells[a].number_of_faces() < shells[b].number_of_faces();
938 hit[i] = surfaces_intersect(shells[a_smaller ? a : b], *trees[a_smaller ? b : a]);
939 });
940 std::vector<char> crossing(n, 0);
941 for (std::size_t i = 0; i < overlaps.size(); ++i)
942 if (hit[i])
943 crossing[overlaps[i].first] = crossing[overlaps[i].second] = 1;
944 progress(95);
945
946 // Crossing shells are solids; any other shell must separate zero from nonzero winding.
947 std::vector<char> drop(n, 0);
948 std::vector<int> target(n, 1);
949 parallel_for_each(n, [&](std::size_t j) {
950 if (crossing[j])
951 return;
952 const Point sample = inner_sample(shells[j]);
953 int winding = 0;
954 for (std::size_t k : enclosing[j])
955 if (CGAL::Side_of_triangle_mesh<Mesh, Kernel, CGAL::Default, Tree>(*trees[k])(sample) == CGAL::ON_BOUNDED_SIDE)
956 winding += crossing[k] ? 1 : sign[k];
957 const bool solid_outside = winding != 0;
958 const bool solid_inside = winding + sign[j] != 0;
959 drop[j] = solid_outside == solid_inside;
960 target[j] = solid_inside ? 1 : -1;
961 });
962 trees.clear();
963
964 for (std::size_t i = 0; i < n; ++i)
965 if (drop[i])
966 ++stats.removed_shells;
967 else if (target[i] != sign[i]) {
968 PMP::reverse_face_orientations(shells[i]);
969 ++stats.reversed_shells;
970 }
971 erase_marked(shells, drop);
972}
Point inner_sample(const Mesh &shell)
Definition MeshRepair.cpp:886
std::vector< std::pair< std::size_t, std::size_t > > overlapping_boxes(std::vector< Box > &boxes)
Definition MeshRepair.cpp:152
bool overlaps(const ExPolygons &expolys1, const ExPolygons &expolys2)
Definition ExPolygon.cpp:192

References bbox_contains(), erase_marked(), inner_sample(), overlapping_boxes(), Slic3r::overlaps(), parallel_for_each(), Slic3r::MeshRepairStats::removed_shells, resolve_shells(), Slic3r::MeshRepairStats::reversed_shells, and signed_volume().

Referenced by repair_soup(), and resolve_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ same_direction()

bool Slic3r::anonymous_namespace{MeshRepair.cpp}::same_direction ( const std::vector< Triangle > & triangles,
const EdgeUse & e1,
const EdgeUse & e2 )
203{
204 return triangles[e1.corner / 3][e1.corner % 3] == triangles[e2.corner / 3][e2.corner % 3];
205}
std::size_t corner
Definition MeshRepair.cpp:173

References Slic3r::anonymous_namespace{MeshRepair.cpp}::EdgeUse::corner, and same_direction().

Referenced by analyze_topology(), and same_direction().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ signed_volume()

double Slic3r::anonymous_namespace{MeshRepair.cpp}::signed_volume ( const Mesh & mesh)
839{
840 double vol = 0.;
841 for (Face f : mesh.faces()) {
842 const Halfedge h = mesh.halfedge(f);
843 vol += CGAL::determinant(mesh.point(mesh.source(h)) - CGAL::ORIGIN, mesh.point(mesh.target(h)) - CGAL::ORIGIN,
844 mesh.point(mesh.target(mesh.next(h))) - CGAL::ORIGIN);
845 }
846 return vol / 6.;
847}

References Slic3r::f(), and signed_volume().

Referenced by remove_local_self_intersections(), repair_soup(), resolve_shells(), and signed_volume().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ sorted_edge_uses()

std::vector< EdgeUse > Slic3r::anonymous_namespace{MeshRepair.cpp}::sorted_edge_uses ( const std::vector< Triangle > & triangles)
178{
179 std::vector<EdgeUse> uses;
180 uses.reserve(3 * triangles.size());
181 for (std::size_t f = 0; f < triangles.size(); ++f)
182 for (std::size_t k = 0; k < 3; ++k) {
183 const auto [a, b] = std::minmax(triangles[f][k], triangles[f][(k + 1) % 3]);
184 uses.push_back({a, b, 3 * f + k});
185 }
186 tbb::parallel_sort(uses.begin(), uses.end());
187 return uses;
188}

References Slic3r::f(), and sorted_edge_uses().

Referenced by analyze_topology(), orient_soup(), and sorted_edge_uses().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ soup_to_meshes()

std::vector< Mesh > Slic3r::anonymous_namespace{MeshRepair.cpp}::soup_to_meshes ( const std::vector< Point > & points,
const std::vector< Triangle > & triangles,
const std::vector< std::vector< std::size_t > > & groups,
bool known_valid )
289{
290 std::vector<Mesh> meshes(groups.size());
291 parallel_for_each(groups.size(), [&](std::size_t i) {
292 std::vector<Point> group_points;
293 std::vector<Triangle> group_triangles;
294 extract_group(points, triangles, groups[i], group_points, group_triangles);
295 if (!known_valid && !PMP::is_polygon_soup_a_polygon_mesh(group_triangles))
296 orient_with_cgal(group_points, group_triangles);
297 PMP::polygon_soup_to_polygon_mesh(group_points, group_triangles, meshes[i]);
298 });
299 return meshes;
300}

References parallel_for_each(), and soup_to_meshes().

Referenced by soup_to_meshes(), split_shells(), and take_closed_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ soup_units()

std::vector< std::vector< std::size_t > > Slic3r::anonymous_namespace{MeshRepair.cpp}::soup_units ( const std::vector< Point > & points,
const std::vector< Triangle > & triangles,
const SoupTopology & topology,
double tolerance )
977{
978 UnionFind parts = connect_vertices(points.size(), triangles);
979 std::vector<std::size_t> part_index(points.size(), NONE);
980 std::vector<std::size_t> part_of_face(triangles.size());
981 std::vector<CGAL::Bbox_3> part_bbox;
982 std::vector<char> part_open;
983 for (std::size_t f = 0; f < triangles.size(); ++f) {
984 std::size_t &index = part_index[parts.find(triangles[f][0])];
985 if (index == NONE) {
986 index = part_bbox.size();
987 part_bbox.emplace_back();
988 part_open.push_back(0);
989 }
990 part_of_face[f] = index;
991 for (std::size_t v : triangles[f])
992 part_bbox[index] += points[v].bbox();
993 part_open[index] = part_open[index] || topology.open_face[f];
994 }
995
996 std::vector<Box> boxes;
997 for (std::size_t i = 0; i < part_bbox.size(); ++i)
998 if (part_open[i]) {
999 const CGAL::Bbox_3 &b = part_bbox[i];
1000 boxes.emplace_back(CGAL::Bbox_3(b.xmin() - tolerance, b.ymin() - tolerance, b.zmin() - tolerance, b.xmax() + tolerance,
1001 b.ymax() + tolerance, b.zmax() + tolerance), i);
1002 }
1003 UnionFind units(part_bbox.size());
1004 for (const auto &[a, b] : overlapping_boxes(boxes))
1005 units.unite(a, b);
1006
1007 std::vector<std::size_t> unit_index(part_bbox.size(), NONE);
1008 std::vector<std::vector<std::size_t>> out;
1009 for (std::size_t f = 0; f < triangles.size(); ++f) {
1010 std::size_t &index = unit_index[units.find(part_of_face[f])];
1011 if (index == NONE) {
1012 index = out.size();
1013 out.emplace_back();
1014 }
1015 out[index].push_back(f);
1016 }
1017 return out;
1018}
std::size_t find(std::size_t i)
Definition MeshRepair.cpp:98

References connect_vertices(), Slic3r::f(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::find(), NONE, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::open_face, overlapping_boxes(), soup_units(), and Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::unite().

Referenced by repair_soup(), and soup_units().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ split_shells()

std::vector< Mesh > Slic3r::anonymous_namespace{MeshRepair.cpp}::split_shells ( Mesh & mesh)
303{
304 mesh.collect_garbage();
305 std::vector<std::size_t> face_part(mesh.number_of_faces());
306 const std::size_t num_parts = PMP::connected_components(mesh, CGAL::make_property_map(face_part));
307 std::vector<std::size_t> part_size(num_parts, 0);
308 for (std::size_t part : face_part)
309 ++part_size[part];
310 const std::size_t largest = std::max_element(part_size.begin(), part_size.end()) - part_size.begin();
311
312 // The small shells are copied out and the largest one stays in place.
313 std::vector<Mesh> shells;
314 if (num_parts > 1) {
315 std::vector<Point> points;
316 for (Vertex v : mesh.vertices())
317 points.push_back(mesh.point(v));
318 std::vector<Triangle> triangles;
319 std::vector<std::vector<std::size_t>> groups(num_parts);
320 for (Face f : mesh.faces())
321 if (face_part[f] != largest) {
322 Triangle &t = triangles.emplace_back();
323 std::size_t k = 0;
324 for (Vertex v : CGAL::vertices_around_face(mesh.halfedge(f), mesh))
325 t[k++] = v;
326 groups[face_part[f]].push_back(triangles.size() - 1);
327 }
328 groups.erase(groups.begin() + largest);
329 std::vector<Halfedge> nonmanifold;
330 PMP::non_manifold_vertices(mesh, std::back_inserter(nonmanifold));
331 shells = soup_to_meshes(points, triangles, groups, nonmanifold.empty());
332 PMP::keep_connected_components(mesh, std::vector<std::size_t>{largest}, CGAL::make_property_map(face_part));
333 mesh.collect_garbage();
334 }
335 shells.insert(shells.begin(), std::move(mesh));
336 return shells;
337}
std::vector< Mesh > soup_to_meshes(const std::vector< Point > &points, const std::vector< Triangle > &triangles, const std::vector< std::vector< std::size_t > > &groups, bool known_valid)
Definition MeshRepair.cpp:287

References Slic3r::f(), soup_to_meshes(), and split_shells().

Referenced by repair_unit(), and split_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ surfaces_intersect()

bool Slic3r::anonymous_namespace{MeshRepair.cpp}::surfaces_intersect ( const Mesh & mesh,
const Tree & other )
877{
878 for (Face f : mesh.faces()) {
879 const Halfedge h = mesh.halfedge(f);
880 if (other.do_intersect(Kernel::Triangle_3(mesh.point(mesh.source(h)), mesh.point(mesh.target(h)), mesh.point(mesh.target(mesh.next(h))))))
881 return true;
882 }
883 return false;
884}

References Slic3r::f(), and surfaces_intersect().

Referenced by surfaces_intersect().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ take_closed_shells()

std::vector< Mesh > Slic3r::anonymous_namespace{MeshRepair.cpp}::take_closed_shells ( const std::vector< Point > & points,
std::vector< Triangle > & triangles )
341{
342 const SoupTopology topology = analyze_topology(points.size(), triangles);
343 UnionFind parts = connect_vertices(points.size(), triangles);
344 std::vector<char> part_ok(points.size(), 1);
345 for (std::size_t f = 0; f < triangles.size(); ++f)
346 for (std::size_t v : triangles[f])
347 if (topology.open_face[f] || topology.bad_face[f] || topology.pinched[v])
348 part_ok[parts.find(v)] = 0;
349
350 std::vector<std::size_t> group_of(points.size(), NONE);
351 std::vector<std::vector<std::size_t>> groups;
352 std::vector<char> taken(triangles.size(), 0);
353 for (std::size_t f = 0; f < triangles.size(); ++f)
354 if (const std::size_t r = parts.find(triangles[f][0]); part_ok[r]) {
355 if (group_of[r] == NONE) {
356 group_of[r] = groups.size();
357 groups.emplace_back();
358 }
359 groups[group_of[r]].push_back(f);
360 taken[f] = 1;
361 }
362 std::vector<Mesh> shells = soup_to_meshes(points, triangles, groups, true);
363 erase_marked(triangles, taken);
364 return shells;
365}

References analyze_topology(), Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::bad_face, connect_vertices(), erase_marked(), Slic3r::f(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::find(), NONE, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::open_face, Slic3r::anonymous_namespace{MeshRepair.cpp}::SoupTopology::pinched, soup_to_meshes(), and take_closed_shells().

Referenced by repair_soup(), and take_closed_shells().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ weld_border_vertices()

std::size_t Slic3r::anonymous_namespace{MeshRepair.cpp}::weld_border_vertices ( const std::vector< Point > & points,
std::vector< Triangle > & triangles,
const std::vector< std::size_t > & border,
double tolerance )
457{
458 if (border.empty())
459 return 0;
460 std::vector<double> radius(points.size(), tolerance);
461 for (const Triangle &t : triangles)
462 for (std::size_t k = 0; k < 3; ++k) {
463 const double r = 0.25 * std::sqrt(CGAL::squared_distance(points[t[k]], points[t[(k + 1) % 3]]));
464 radius[t[k]] = std::min(radius[t[k]], r);
465 radius[t[(k + 1) % 3]] = std::min(radius[t[(k + 1) % 3]], r);
466 }
467
468 using Cell = std::array<int64_t, 3>;
469 auto cell_of = [tolerance](const Point &p) {
470 return Cell{int64_t(std::floor(p.x() / tolerance)), int64_t(std::floor(p.y() / tolerance)), int64_t(std::floor(p.z() / tolerance))};
471 };
472 std::vector<std::pair<Cell, std::size_t>> grid;
473 for (std::size_t v : border)
474 grid.emplace_back(cell_of(points[v]), v);
475 tbb::parallel_sort(grid.begin(), grid.end());
476
477 UnionFind uf(points.size());
478 for (const auto &[c, v] : grid)
479 for (int64_t dx = -1; dx <= 1; ++dx)
480 for (int64_t dy = -1; dy <= 1; ++dy)
481 for (int64_t dz = -1; dz <= 1; ++dz) {
482 const Cell n{c[0] + dx, c[1] + dy, c[2] + dz};
483 for (auto it = std::lower_bound(grid.begin(), grid.end(), std::make_pair(n, std::size_t(0))); it != grid.end() && it->first == n; ++it)
484 if (const std::size_t u = it->second;
485 u < v && CGAL::squared_distance(points[u], points[v]) <= CGAL::square(std::min(radius[u], radius[v])))
486 uf.unite(u, v);
487 }
488 std::size_t welded = 0;
489 for (std::size_t v : border)
490 welded += uf.find(v) != v;
491 if (welded > 0)
492 for (Triangle &t : triangles)
493 for (std::size_t &v : t)
494 v = uf.find(v);
495 return welded;
496}
std::vector< ArithmeticOnly< T > > grid(const T &start, const T &stop, const T &stride)
A set of equidistant values starting from 'start' (inclusive), ending in the closest multiple of 'str...
Definition MTUtils.hpp:127
Definition ExtrusionSimulator.cpp:627

References Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::find(), Slic3r::grid(), Slic3r::anonymous_namespace{MeshRepair.cpp}::UnionFind::unite(), and weld_border_vertices().

Referenced by repair_soup(), and weld_border_vertices().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ NONE

std::size_t Slic3r::anonymous_namespace{MeshRepair.cpp}::NONE = std::size_t(-1)
constexpr