From 5db91f1944ff2def24a9e548b25024873f211f4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 23 Mar 2022 11:49:26 +0100 Subject: [PATCH] Added detection for a missing Voronoi vertex. If the missing Voronoi vertex is detected, try to resolve it by rotation input polygons. --- .../Arachne/SkeletalTrapezoidation.cpp | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp index fabdc2486f..c07dafd542 100644 --- a/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp +++ b/src/libslic3r/Arachne/SkeletalTrapezoidation.cpp @@ -378,6 +378,52 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const Bead constructFromPolygons(polys); } +bool detect_missing_voronoi_vertex(const Geometry::VoronoiDiagram &voronoi_diagram, const std::vector &segments) { + for (VoronoiUtils::vd_t::cell_type cell : voronoi_diagram.cells()) { + if (!cell.incident_edge()) + continue; // There is no spoon + + if (cell.contains_segment()) { + const SkeletalTrapezoidation::Segment &source_segment = VoronoiUtils::getSourceSegment(cell, segments); + const Point from = source_segment.from(); + const Point to = source_segment.to(); + + // Find starting edge + // Find end edge + bool seen_possible_start = false; + bool after_start = false; + bool ending_edge_is_set_before_start = false; + VoronoiUtils::vd_t::edge_type *starting_vd_edge = nullptr; + VoronoiUtils::vd_t::edge_type *ending_vd_edge = nullptr; + VoronoiUtils::vd_t::edge_type *edge = cell.incident_edge(); + do { + if (edge->is_infinite()) continue; + + Vec2i64 v0 = VoronoiUtils::p(edge->vertex0()); + Vec2i64 v1 = VoronoiUtils::p(edge->vertex1()); + + assert(!(v0 == to.cast() && v1 == from.cast())); + if (v0 == to.cast() && !after_start) { // Use the last edge which starts in source_segment.to + starting_vd_edge = edge; + seen_possible_start = true; + } else if (seen_possible_start) { + after_start = true; + } + + if (v1 == from.cast() && (!ending_vd_edge || ending_edge_is_set_before_start)) { + ending_edge_is_set_before_start = !after_start; + ending_vd_edge = edge; + } + } while (edge = edge->next(), edge != cell.incident_edge()); + + if (!starting_vd_edge || !ending_vd_edge || starting_vd_edge == ending_vd_edge) + return true; + } + } + + return false; +} + void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) { // Check self intersections. @@ -399,8 +445,37 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) Geometry::VoronoiDiagram voronoi_diagram; construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); - for (vd_t::cell_type cell : voronoi_diagram.cells()) - { + // Try to detect cases when some Voronoi vertex is missing. + // When any Voronoi vertex is missing, rotate input polygon and try again. + const bool has_missing_voronoi_vertex = detect_missing_voronoi_vertex(voronoi_diagram, segments); + const double fix_angle = PI / 6; + std::unordered_map vertex_mapping; + Polygons polys_copy = polys; + if (has_missing_voronoi_vertex) { + BOOST_LOG_TRIVIAL(debug) << "Detected missing Voronoi vertex, input polygons will be rotated back and forth."; + for (Polygon &poly : polys_copy) + poly.rotate(fix_angle); + + assert(polys_copy.size() == polys.size()); + for (size_t poly_idx = 0; poly_idx < polys.size(); ++poly_idx) { + assert(polys_copy[poly_idx].size() == polys[poly_idx].size()); + for (size_t point_idx = 0; point_idx < polys[poly_idx].size(); ++point_idx) + vertex_mapping.insert({polys[poly_idx][point_idx], polys_copy[poly_idx][point_idx]}); + } + + segments.clear(); + for (size_t poly_idx = 0; poly_idx < polys_copy.size(); poly_idx++) + for (size_t point_idx = 0; point_idx < polys_copy[poly_idx].size(); point_idx++) + segments.emplace_back(&polys_copy, poly_idx, point_idx); + + voronoi_diagram.clear(); + construct_voronoi(segments.begin(), segments.end(), &voronoi_diagram); + assert(!detect_missing_voronoi_vertex(voronoi_diagram, segments)); + if (detect_missing_voronoi_vertex(voronoi_diagram, segments)) + BOOST_LOG_TRIVIAL(error) << "Detected missing Voronoi vertex even after the rotation of input."; + } + + for (vd_t::cell_type cell : voronoi_diagram.cells()) { if (!cell.incident_edge()) continue; // There is no spoon @@ -455,6 +530,16 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys) prev_edge->to->data.distance_to_boundary = 0; } + if (has_missing_voronoi_vertex) { + for (node_t &node : graph.nodes) { + // If a mapping exists between a rotated point and an original point, use this mapping. Otherwise, rotate a point in the opposite direction. + if (auto node_it = vertex_mapping.find(node.p); node_it != vertex_mapping.end()) + node.p = node_it->second; + else + node.p.rotate(-fix_angle); + } + } + separatePointyQuadEndNodes(); graph.fixNodeDuplication();