SPE-2298: Fix crash caused by a numerical issue during testing if a Voronoi vertex is inside a corner of a polygon.

Cherry-picked from prusa3d/PrusaSlicer@669c931b77

Co-authored-by: Lukáš Hejl <hejl.lukas@gmail.com>
This commit is contained in:
Noisyfox
2024-12-22 16:44:36 +08:00
parent 3a43050ad1
commit 862acea2af
9 changed files with 181 additions and 144 deletions

View File

@@ -2,6 +2,7 @@
#include <Arachne/utils/PolygonsSegmentIndex.hpp>
#include <MultiMaterialSegmentation.hpp>
#include <Geometry.hpp>
#include "VoronoiUtils.hpp"
#include "libslic3r.h"
@@ -28,6 +29,7 @@ template SegmentCellRange<Point> VoronoiUtils::compute_segment_cell_range(const
template SegmentCellRange<Point> VoronoiUtils::compute_segment_cell_range(const VoronoiDiagram::cell_type &, VD::SegmentIt, VD::SegmentIt);
template SegmentCellRange<Point> VoronoiUtils::compute_segment_cell_range(const VoronoiDiagram::cell_type &, ColoredLinesConstIt, ColoredLinesConstIt);
template SegmentCellRange<Point> VoronoiUtils::compute_segment_cell_range(const VoronoiDiagram::cell_type &, PolygonsSegmentIndexConstIt, PolygonsSegmentIndexConstIt);
template PointCellRange<Point> VoronoiUtils::compute_point_cell_range(const VoronoiDiagram::cell_type &, PolygonsSegmentIndexConstIt, PolygonsSegmentIndexConstIt);
template Points VoronoiUtils::discretize_parabola(const Point &, const Arachne::PolygonsSegmentIndex &, const Point &, const Point &, coord_t, float);
template Arachne::PolygonsPointIndex VoronoiUtils::get_source_point_index(const VoronoiDiagram::cell_type &, PolygonsSegmentIndexConstIt, PolygonsSegmentIndexConstIt);
@@ -245,6 +247,62 @@ VoronoiUtils::compute_segment_cell_range(const VD::cell_type &cell, const Segmen
return cell_range;
}
template<typename SegmentIterator>
typename boost::polygon::enable_if<
typename boost::polygon::gtl_if<typename boost::polygon::is_segment_concept<
typename boost::polygon::geometry_concept<typename std::iterator_traits<SegmentIterator>::value_type>::type>::type>::type,
Geometry::PointCellRange<
typename boost::polygon::segment_point_type<typename std::iterator_traits<SegmentIterator>::value_type>::type>>::type
VoronoiUtils::compute_point_cell_range(const VD::cell_type &cell, const SegmentIterator segment_begin, const SegmentIterator segment_end)
{
using Segment = typename std::iterator_traits<SegmentIterator>::value_type;
using Point = typename boost::polygon::segment_point_type<Segment>::type;
using PointCellRange = PointCellRange<Point>;
using CoordType = typename Point::coord_type;
const Point source_point = Geometry::VoronoiUtils::get_source_point(cell, segment_begin, segment_end);
// We want to ignore (by returning PointCellRange without assigned edge_begin and edge_end) cells outside the input polygon.
PointCellRange cell_range(source_point);
const VD::edge_type *edge = cell.incident_edge();
if (edge->is_infinite() || !is_in_range<CoordType>(*edge)) {
// Ignore infinite edges, because they only occur outside the polygon.
// Also ignore edges with endpoints that don't fit into CoordType, because such edges are definitely outside the polygon.
return cell_range;
}
const Arachne::PolygonsPointIndex source_point_idx = Geometry::VoronoiUtils::get_source_point_index(cell, segment_begin, segment_end);
const Point edge_v0 = Geometry::VoronoiUtils::to_point(edge->vertex0()).template cast<CoordType>();
const Point edge_v1 = Geometry::VoronoiUtils::to_point(edge->vertex1()).template cast<CoordType>();
const Point edge_query_point = (edge_v0 == source_point) ? edge_v1 : edge_v0;
// Check if the edge has another endpoint inside the corner of the polygon.
if (!Geometry::is_point_inside_polygon_corner(source_point_idx.prev().p(), source_point_idx.p(), source_point_idx.next().p(), edge_query_point)) {
// If the endpoint isn't inside the corner of the polygon, it means that
// the whole cell isn't inside the polygons, and we will ignore such cells.
return cell_range;
}
const Vec2i64 source_point_i64 = source_point.template cast<int64_t>();
edge = cell.incident_edge();
do {
assert(edge->is_finite());
if (Vec2i64 v1 = Geometry::VoronoiUtils::to_point(edge->vertex1()); v1 == source_point_i64) {
cell_range.edge_begin = edge->next();
cell_range.edge_end = edge;
} else {
// FIXME @hejllukas: With Arachne, we don't support polygons with collinear edges,
// because with collinear edges we have to handle secondary edges.
// Such edges goes through the endpoints of the input segments.
assert((Geometry::VoronoiUtils::to_point(edge->vertex0()) == source_point_i64 || edge->is_primary()) && "Point cells must end in the point! They cannot cross the point with an edge, because collinear edges are not allowed in the input.");
}
} while (edge = edge->next(), edge != cell.incident_edge());
return cell_range;
}
Vec2i64 VoronoiUtils::to_point(const VD::vertex_type *vertex)
{
assert(vertex != nullptr);