mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-13 09:47:58 -06:00
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:
parent
3a43050ad1
commit
862acea2af
9 changed files with 181 additions and 144 deletions
|
@ -7,7 +7,6 @@
|
|||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "utils/linearAlg2D.hpp"
|
||||
|
@ -104,7 +103,7 @@ SkeletalTrapezoidation::node_t &SkeletalTrapezoidation::makeNode(const VD::verte
|
|||
}
|
||||
}
|
||||
|
||||
void SkeletalTrapezoidation::transferEdge(Point from, Point to, const VD::edge_type &vd_edge, edge_t *&prev_edge, Point &start_source_point, Point &end_source_point, const std::vector<Segment> &segments) {
|
||||
void SkeletalTrapezoidation::transferEdge(const Point &from, const Point &to, const VD::edge_type &vd_edge, edge_t *&prev_edge, const Point &start_source_point, const Point &end_source_point, const std::vector<Segment> &segments) {
|
||||
auto he_edge_it = vd_edge_to_he_edge.find(vd_edge.twin());
|
||||
if (he_edge_it != vd_edge_to_he_edge.end())
|
||||
{ // Twin segment(s) have already been made
|
||||
|
@ -323,50 +322,6 @@ Points SkeletalTrapezoidation::discretize(const VD::edge_type& vd_edge, const st
|
|||
}
|
||||
}
|
||||
|
||||
bool SkeletalTrapezoidation::computePointCellRange(const VD::cell_type &cell, Point &start_source_point, Point &end_source_point, const VD::edge_type *&starting_vd_edge, const VD::edge_type *&ending_vd_edge, const std::vector<Segment> &segments) {
|
||||
if (cell.incident_edge()->is_infinite())
|
||||
return false; //Infinite edges only occur outside of the polygon. Don't copy any part of this cell.
|
||||
|
||||
// Check if any point of the cell is inside or outside polygon
|
||||
// Copy whole cell into graph or not at all
|
||||
|
||||
// If the cell.incident_edge()->vertex0() is far away so much that it doesn't even fit into Vec2i64, then there is no way that it will be inside the input polygon.
|
||||
if (const VD::vertex_type &vert = *cell.incident_edge()->vertex0();
|
||||
vert.x() >= double(std::numeric_limits<int64_t>::max()) || vert.x() <= double(std::numeric_limits<int64_t>::lowest()) ||
|
||||
vert.y() >= double(std::numeric_limits<int64_t>::max()) || vert.y() <= double(std::numeric_limits<int64_t>::lowest()))
|
||||
return false; // Don't copy any part of this cell
|
||||
|
||||
const Point source_point = Geometry::VoronoiUtils::get_source_point(cell, segments.begin(), segments.end());
|
||||
const PolygonsPointIndex source_point_index = Geometry::VoronoiUtils::get_source_point_index(cell, segments.begin(), segments.end());
|
||||
Vec2i64 some_point = Geometry::VoronoiUtils::to_point(cell.incident_edge()->vertex0());
|
||||
if (some_point == source_point.cast<int64_t>())
|
||||
some_point = Geometry::VoronoiUtils::to_point(cell.incident_edge()->vertex1());
|
||||
|
||||
//Test if the some_point is even inside the polygon.
|
||||
//The edge leading out of a polygon must have an endpoint that's not in the corner following the contour of the polygon at that vertex.
|
||||
//So if it's inside the corner formed by the polygon vertex, it's all fine.
|
||||
//But if it's outside of the corner, it must be a vertex of the Voronoi diagram that goes outside of the polygon towards infinity.
|
||||
if (!LinearAlg2D::isInsideCorner(source_point_index.prev().p(), source_point_index.p(), source_point_index.next().p(), some_point))
|
||||
return false; // Don't copy any part of this cell
|
||||
|
||||
const VD::edge_type* vd_edge = cell.incident_edge();
|
||||
do {
|
||||
assert(vd_edge->is_finite());
|
||||
if (Vec2i64 p1 = Geometry::VoronoiUtils::to_point(vd_edge->vertex1()); p1 == source_point.cast<int64_t>()) {
|
||||
start_source_point = source_point;
|
||||
end_source_point = source_point;
|
||||
starting_vd_edge = vd_edge->next();
|
||||
ending_vd_edge = vd_edge;
|
||||
} else {
|
||||
assert((Geometry::VoronoiUtils::to_point(vd_edge->vertex0()) == source_point.cast<int64_t>() || !vd_edge->is_secondary()) && "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 (vd_edge = vd_edge->next(), vd_edge != cell.incident_edge());
|
||||
assert(starting_vd_edge && ending_vd_edge);
|
||||
assert(starting_vd_edge != ending_vd_edge);
|
||||
return true;
|
||||
}
|
||||
|
||||
SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const BeadingStrategy& beading_strategy,
|
||||
double transitioning_angle, coord_t discretization_step_size,
|
||||
coord_t transition_filter_dist, coord_t allowed_filter_deviation,
|
||||
|
@ -434,15 +389,20 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
|||
// Compute and store result in above variables
|
||||
|
||||
if (cell.contains_point()) {
|
||||
const bool keep_going = computePointCellRange(cell, start_source_point, end_source_point, starting_voronoi_edge, ending_voronoi_edge, segments);
|
||||
if (!keep_going)
|
||||
Geometry::PointCellRange<Point> cell_range = Geometry::VoronoiUtils::compute_point_cell_range(cell, segments.cbegin(), segments.cend());
|
||||
start_source_point = cell_range.source_point;
|
||||
end_source_point = cell_range.source_point;
|
||||
starting_voronoi_edge = cell_range.edge_begin;
|
||||
ending_voronoi_edge = cell_range.edge_end;
|
||||
|
||||
if (!cell_range.is_valid())
|
||||
continue;
|
||||
} else {
|
||||
assert(cell.contains_segment());
|
||||
Geometry::SegmentCellRange<Point> cell_range = Geometry::VoronoiUtils::compute_segment_cell_range(cell, segments.cbegin(), segments.cend());
|
||||
assert(cell_range.is_valid());
|
||||
start_source_point = cell_range.segment_start_point;
|
||||
end_source_point = cell_range.segment_end_point;
|
||||
start_source_point = cell_range.source_segment_start_point;
|
||||
end_source_point = cell_range.source_segment_end_point;
|
||||
starting_voronoi_edge = cell_range.edge_begin;
|
||||
ending_voronoi_edge = cell_range.edge_end;
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ protected:
|
|||
* Transfer an edge from the VD to the HE and perform discretization of parabolic edges (and vertex-vertex edges)
|
||||
* \p prev_edge serves as input and output. May be null as input.
|
||||
*/
|
||||
void transferEdge(Point from, Point to, const VD::edge_type &vd_edge, edge_t *&prev_edge, Point &start_source_point, Point &end_source_point, const std::vector<Segment> &segments);
|
||||
void transferEdge(const Point &from, const Point &to, const VD::edge_type &vd_edge, edge_t *&prev_edge, const Point &start_source_point, const Point &end_source_point, const std::vector<Segment> &segments);
|
||||
|
||||
/*!
|
||||
* Discretize a Voronoi edge that represents the medial axis of a vertex-
|
||||
|
@ -211,32 +211,6 @@ protected:
|
|||
*/
|
||||
Points discretize(const VD::edge_type& segment, const std::vector<Segment>& segments);
|
||||
|
||||
/*!
|
||||
* Compute the range of line segments that surround a cell of the skeletal
|
||||
* graph that belongs to a point on the medial axis.
|
||||
*
|
||||
* This should only be used on cells that belong to a corner in the skeletal
|
||||
* graph, e.g. triangular cells, not trapezoid cells.
|
||||
*
|
||||
* The resulting line segments is just the first and the last segment. They
|
||||
* are linked to the neighboring segments, so you can iterate over the
|
||||
* segments until you reach the last segment.
|
||||
* \param cell The cell to compute the range of line segments for.
|
||||
* \param[out] start_source_point The start point of the source segment of
|
||||
* this cell.
|
||||
* \param[out] end_source_point The end point of the source segment of this
|
||||
* cell.
|
||||
* \param[out] starting_vd_edge The edge of the Voronoi diagram where the
|
||||
* loop around the cell starts.
|
||||
* \param[out] ending_vd_edge The edge of the Voronoi diagram where the loop
|
||||
* around the cell ends.
|
||||
* \param points All vertices of the input Polygons.
|
||||
* \param segments All edges of the input Polygons.
|
||||
* /return Whether the cell is inside of the polygon. If it's outside of the
|
||||
* polygon we should skip processing it altogether.
|
||||
*/
|
||||
static bool computePointCellRange(const VD::cell_type &cell, Point &start_source_point, Point &end_source_point, const VD::edge_type *&starting_vd_edge, const VD::edge_type *&ending_vd_edge, const std::vector<Segment> &segments);
|
||||
|
||||
/*!
|
||||
* For VD cells associated with an input polygon vertex, we need to separate the node at the end and start of the cell into two
|
||||
* That way we can reach both the quad_start and the quad_end from the [incident_edge] of the two new nodes
|
||||
|
|
|
@ -315,8 +315,7 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist)
|
|||
}
|
||||
}
|
||||
|
||||
void SkeletalTrapezoidationGraph::makeRib(edge_t*& prev_edge, Point start_source_point, Point end_source_point)
|
||||
{
|
||||
void SkeletalTrapezoidationGraph::makeRib(edge_t *&prev_edge, const Point &start_source_point, const Point &end_source_point) {
|
||||
Point p;
|
||||
Line(start_source_point, end_source_point).distance_to_infinite_squared(prev_edge->to->p, &p);
|
||||
coord_t dist = (prev_edge->to->p - p).cast<int64_t>().norm();
|
||||
|
|
|
@ -83,7 +83,7 @@ public:
|
|||
*/
|
||||
void collapseSmallEdges(coord_t snap_dist = 5);
|
||||
|
||||
void makeRib(edge_t*& prev_edge, Point start_source_point, Point end_source_point);
|
||||
void makeRib(edge_t*& prev_edge, const Point &start_source_point, const Point &end_source_point);
|
||||
|
||||
/*!
|
||||
* Insert a node into the graph and connect it to the input polygon using ribs
|
||||
|
|
|
@ -9,63 +9,6 @@
|
|||
namespace Slic3r::Arachne::LinearAlg2D
|
||||
{
|
||||
|
||||
/*!
|
||||
* Test whether a point is inside a corner.
|
||||
* Whether point \p query_point is left of the corner abc.
|
||||
* Whether the \p query_point is in the circle half left of ab and left of bc, rather than to the right.
|
||||
*
|
||||
* Test whether the \p query_point is inside of a polygon w.r.t a single corner.
|
||||
*/
|
||||
inline static bool isInsideCorner(const Point &a, const Point &b, const Point &c, const Vec2i64 &query_point)
|
||||
{
|
||||
// Visualisation for the algorithm below:
|
||||
//
|
||||
// query
|
||||
// |
|
||||
// |
|
||||
// |
|
||||
// perp-----------b
|
||||
// / \ (note that the lines
|
||||
// / \ AB and AC are normalized
|
||||
// / \ to 10000 units length)
|
||||
// a c
|
||||
//
|
||||
|
||||
auto normal = [](const Point &p0, coord_t len) -> Point {
|
||||
int64_t _len = p0.norm();
|
||||
if (_len < 1)
|
||||
return {len, 0};
|
||||
return (p0.cast<int64_t>() * int64_t(len) / _len).cast<coord_t>();
|
||||
};
|
||||
|
||||
auto rotate_90_degree_ccw = [](const Vec2d &p) -> Vec2d {
|
||||
return {-p.y(), p.x()};
|
||||
};
|
||||
|
||||
constexpr coord_t normal_length = 10000; //Create a normal vector of reasonable length in order to reduce rounding error.
|
||||
const Point ba = normal(a - b, normal_length);
|
||||
const Point bc = normal(c - b, normal_length);
|
||||
const Vec2d bq = query_point.cast<double>() - b.cast<double>();
|
||||
const Vec2d perpendicular = rotate_90_degree_ccw(bq); //The query projects to this perpendicular to coordinate 0.
|
||||
|
||||
const double project_a_perpendicular = ba.cast<double>().dot(perpendicular); //Project vertex A on the perpendicular line.
|
||||
const double project_c_perpendicular = bc.cast<double>().dot(perpendicular); //Project vertex C on the perpendicular line.
|
||||
if ((project_a_perpendicular > 0.) != (project_c_perpendicular > 0.)) //Query is between A and C on the projection.
|
||||
{
|
||||
return project_a_perpendicular > 0.; //Due to the winding order of corner ABC, this means that the query is inside.
|
||||
}
|
||||
else //Beyond either A or C, but it could still be inside of the polygon.
|
||||
{
|
||||
const double project_a_parallel = ba.cast<double>().dot(bq); //Project not on the perpendicular, but on the original.
|
||||
const double project_c_parallel = bc.cast<double>().dot(bq);
|
||||
|
||||
//Either:
|
||||
// * A is to the right of B (project_a_perpendicular > 0) and C is below A (project_c_parallel < project_a_parallel), or
|
||||
// * A is to the left of B (project_a_perpendicular < 0) and C is above A (project_c_parallel > project_a_parallel).
|
||||
return (project_c_parallel < project_a_parallel) == (project_a_perpendicular > 0.);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the determinant of the 2D matrix defined by the the vectors ab and ap as rows.
|
||||
*
|
||||
|
|
|
@ -839,4 +839,50 @@ TransformationSVD::TransformationSVD(const Transform3d& trafo)
|
|||
return curMat;
|
||||
}
|
||||
|
||||
bool is_point_inside_polygon_corner(const Point &a, const Point &b, const Point &c, const Point &query_point) {
|
||||
// Cast all input points into int64_t to prevent overflows when points are close to max values of coord_t.
|
||||
const Vec2i64 a_i64 = a.cast<int64_t>();
|
||||
const Vec2i64 b_i64 = b.cast<int64_t>();
|
||||
const Vec2i64 c_i64 = c.cast<int64_t>();
|
||||
const Vec2i64 query_point_i64 = query_point.cast<int64_t>();
|
||||
|
||||
// Shift all points to have a base in vertex B.
|
||||
// Then construct normalized vectors to ensure that we will work with vectors with endpoints on the unit circle.
|
||||
const Vec2d ba = (a_i64 - b_i64).cast<double>().normalized();
|
||||
const Vec2d bc = (c_i64 - b_i64).cast<double>().normalized();
|
||||
const Vec2d bq = (query_point_i64 - b_i64).cast<double>().normalized();
|
||||
|
||||
// Points A and C has to be different.
|
||||
assert(ba != bc);
|
||||
|
||||
// Construct a normal for the vector BQ that points to the left side of the vector BQ.
|
||||
const Vec2d bq_left_normal = perp(bq);
|
||||
|
||||
const double proj_a_on_bq_normal = ba.dot(bq_left_normal); // Project point A on the normal of BQ.
|
||||
const double proj_c_on_bq_normal = bc.dot(bq_left_normal); // Project point C on the normal of BQ.
|
||||
if ((proj_a_on_bq_normal > 0. && proj_c_on_bq_normal <= 0.) || (proj_a_on_bq_normal <= 0. && proj_c_on_bq_normal > 0.)) {
|
||||
// Q is between points A and C or lies on one of those vectors (BA or BC).
|
||||
|
||||
// Based on the CCW order of polygons (contours) and order of corner ABC,
|
||||
// when this condition is met, the query point is inside the corner.
|
||||
return proj_a_on_bq_normal > 0.;
|
||||
} else {
|
||||
// Q isn't between points A and C, but still it can be inside the corner.
|
||||
|
||||
const double proj_a_on_bq = ba.dot(bq); // Project point A on BQ.
|
||||
const double proj_c_on_bq = bc.dot(bq); // Project point C on BQ.
|
||||
|
||||
// The value of proj_a_on_bq_normal is the same when we project the vector BA on the normal of BQ.
|
||||
// So we can say that the Q is on the right side of the vector BA when proj_a_on_bq_normal > 0, and
|
||||
// that the Q is on the left side of the vector BA proj_a_on_bq_normal < 0.
|
||||
// Also, the Q is on the right side of the bisector of oriented angle ABC when proj_c_on_bq < proj_a_on_bq, and
|
||||
// the Q is on the left side of the bisector of oriented angle ABC when proj_c_on_bq > proj_a_on_bq.
|
||||
|
||||
// So the Q is inside the corner when one of the following conditions is met:
|
||||
// * The Q is on the right side of the vector BA, and the Q is on the right side of the bisector of the oriented angle ABC.
|
||||
// * The Q is on the left side of the vector BA, and the Q is on the left side of the bisector of the oriented angle ABC.
|
||||
return (proj_a_on_bq_normal > 0. && proj_c_on_bq < proj_a_on_bq) || (proj_a_on_bq_normal <= 0. && proj_c_on_bq >= proj_a_on_bq);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::Geometry
|
||||
|
|
|
@ -545,6 +545,23 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
|||
}
|
||||
|
||||
Transformation mat_around_a_point_rotate(const Transformation& innMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian);
|
||||
|
||||
/**
|
||||
* Checks if a given point is inside a corner of a polygon.
|
||||
*
|
||||
* The corner of a polygon is defined by three points A, B, C in counterclockwise order.
|
||||
*
|
||||
* Adapted from CuraEngine LinearAlg2D::isInsideCorner by Tim Kuipers @BagelOrb
|
||||
* and @Ghostkeeper.
|
||||
*
|
||||
* @param a The first point of the corner.
|
||||
* @param b The second point of the corner (the common vertex of the two edges forming the corner).
|
||||
* @param c The third point of the corner.
|
||||
* @param query_point The point to be checked if is inside the corner.
|
||||
* @return True if the query point is inside the corner, false otherwise.
|
||||
*/
|
||||
bool is_point_inside_polygon_corner(const Point &a, const Point &b, const Point &c, const Point &query_point);
|
||||
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -11,15 +11,28 @@ namespace Slic3r::Geometry {
|
|||
// Represent trapezoid Voronoi cell around segment.
|
||||
template<typename PT> struct SegmentCellRange
|
||||
{
|
||||
const PT segment_start_point; // The start point of the source segment of this cell.
|
||||
const PT segment_end_point; // The end point of the source segment of this cell.
|
||||
const PT source_segment_start_point; // The start point of the source segment of this cell.
|
||||
const PT source_segment_end_point; // The end point of the source segment of this cell.
|
||||
const VD::edge_type *edge_begin = nullptr; // The edge of the Voronoi diagram where the loop around the cell starts.
|
||||
const VD::edge_type *edge_end = nullptr; // The edge of the Voronoi diagram where the loop around the cell ends.
|
||||
|
||||
SegmentCellRange() = delete;
|
||||
explicit SegmentCellRange(const PT &source_segment_start_point, const PT &source_segment_end_point)
|
||||
: source_segment_start_point(source_segment_start_point), source_segment_end_point(source_segment_end_point)
|
||||
{}
|
||||
|
||||
bool is_valid() const { return edge_begin && edge_end && edge_begin != edge_end; }
|
||||
};
|
||||
|
||||
// Represent trapezoid Voronoi cell around point.
|
||||
template<typename PT> struct PointCellRange
|
||||
{
|
||||
const PT source_point; // The source point of this cell.
|
||||
const VD::edge_type *edge_begin = nullptr; // The edge of the Voronoi diagram where the loop around the cell starts.
|
||||
const VD::edge_type *edge_end = nullptr; // The edge of the Voronoi diagram where the loop around the cell ends.
|
||||
|
||||
SegmentCellRange() = delete;
|
||||
explicit SegmentCellRange(const PT &segment_start_point, const PT &segment_end_point)
|
||||
: segment_start_point(segment_start_point), segment_end_point(segment_end_point)
|
||||
{}
|
||||
PointCellRange() = delete;
|
||||
explicit PointCellRange(const PT &source_point) : source_point(source_point) {}
|
||||
|
||||
bool is_valid() const { return edge_begin && edge_end && edge_begin != edge_end; }
|
||||
};
|
||||
|
@ -80,7 +93,7 @@ public:
|
|||
* are linked to the neighboring segments, so you can iterate over the
|
||||
* segments until you reach the last segment.
|
||||
*
|
||||
* Adapted from CuraEngine VoronoiUtils::computePointCellRange by Tim Kuipers @BagelOrb,
|
||||
* Adapted from CuraEngine VoronoiUtils::computeSegmentCellRange by Tim Kuipers @BagelOrb,
|
||||
* Jaime van Kessel @nallath, Remco Burema @rburema and @Ghostkeeper.
|
||||
*
|
||||
* @param cell The cell to compute the range of line segments for.
|
||||
|
@ -96,6 +109,33 @@ public:
|
|||
typename boost::polygon::segment_point_type<typename std::iterator_traits<SegmentIterator>::value_type>::type>>::type
|
||||
compute_segment_cell_range(const VD::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end);
|
||||
|
||||
/**
|
||||
* Compute the range of line segments that surround a cell of the skeletal
|
||||
* graph that belongs to a point on the medial axis.
|
||||
*
|
||||
* This should only be used on cells that belong to a corner in the skeletal
|
||||
* graph, e.g. triangular cells, not trapezoid cells.
|
||||
*
|
||||
* The resulting line segments is just the first and the last segment. They
|
||||
* are linked to the neighboring segments, so you can iterate over the
|
||||
* segments until you reach the last segment.
|
||||
*
|
||||
* Adapted from CuraEngine VoronoiUtils::computePointCellRange by Tim Kuipers @BagelOrb
|
||||
* Jaime van Kessel @nallath, Remco Burema @rburema and @Ghostkeeper.
|
||||
*
|
||||
* @param cell The cell to compute the range of line segments for.
|
||||
* @param segment_begin Begin iterator for all edges of the input Polygons.
|
||||
* @param segment_end End iterator for all edges of the input Polygons.
|
||||
* @return Range of line segments that surround the cell.
|
||||
*/
|
||||
template<typename SegmentIterator>
|
||||
static 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
|
||||
compute_point_cell_range(const VD::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end);
|
||||
|
||||
template<typename T> static bool is_in_range(double value)
|
||||
{
|
||||
return double(std::numeric_limits<T>::lowest()) <= value && value <= double(std::numeric_limits<T>::max());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue