mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-13 22:55:53 +08:00
Generalize all functions in VoronoiUtils to not depend on Arachne data structures.
Also, move VoronoiUtils from Arachne namespace to Geometry namespace.
This commit is contained in:
parent
23b7c41857
commit
ac33876796
@ -10,8 +10,6 @@
|
||||
#include <functional>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "utils/VoronoiUtils.hpp"
|
||||
|
||||
#include "utils/linearAlg2D.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "SVG.hpp"
|
||||
@ -19,6 +17,8 @@
|
||||
#include "Geometry/VoronoiUtilsCgal.hpp"
|
||||
#include "../EdgeGrid.hpp"
|
||||
|
||||
#include "Geometry/VoronoiUtils.hpp"
|
||||
|
||||
#define SKELETAL_TRAPEZOIDATION_BEAD_SEARCH_MAX 1000 //A limit to how long it'll keep searching for adjacent beads. Increasing will re-use beadings more often (saving performance), but search longer for beading (costing performance).
|
||||
|
||||
namespace Slic3r::Arachne
|
||||
@ -218,20 +218,17 @@ void SkeletalTrapezoidation::transferEdge(Point from, Point to, VD::edge_type& v
|
||||
|
||||
Points SkeletalTrapezoidation::discretize(const VD::edge_type& vd_edge, const std::vector<Segment>& segments)
|
||||
{
|
||||
assert(Geometry::VoronoiUtils::is_in_range<coord_t>(vd_edge));
|
||||
|
||||
/*Terminology in this function assumes that the edge moves horizontally from
|
||||
left to right. This is not necessarily the case; the edge can go in any
|
||||
direction, but it helps to picture it in a certain direction in your head.*/
|
||||
|
||||
const VD::cell_type* left_cell = vd_edge.cell();
|
||||
const VD::cell_type* right_cell = vd_edge.twin()->cell();
|
||||
const VD::cell_type *left_cell = vd_edge.cell();
|
||||
const VD::cell_type *right_cell = vd_edge.twin()->cell();
|
||||
|
||||
assert(VoronoiUtils::p(vd_edge.vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge.vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
|
||||
assert(VoronoiUtils::p(vd_edge.vertex0()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge.vertex0()).y() >= std::numeric_limits<coord_t>::lowest());
|
||||
assert(VoronoiUtils::p(vd_edge.vertex1()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge.vertex1()).x() >= std::numeric_limits<coord_t>::lowest());
|
||||
assert(VoronoiUtils::p(vd_edge.vertex1()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge.vertex1()).y() >= std::numeric_limits<coord_t>::lowest());
|
||||
|
||||
Point start = VoronoiUtils::p(vd_edge.vertex0()).cast<coord_t>();
|
||||
Point end = VoronoiUtils::p(vd_edge.vertex1()).cast<coord_t>();
|
||||
Point start = Geometry::VoronoiUtils::to_point(vd_edge.vertex0()).cast<coord_t>();
|
||||
Point end = Geometry::VoronoiUtils::to_point(vd_edge.vertex1()).cast<coord_t>();
|
||||
|
||||
bool point_left = left_cell->contains_point();
|
||||
bool point_right = right_cell->contains_point();
|
||||
@ -241,20 +238,20 @@ Points SkeletalTrapezoidation::discretize(const VD::edge_type& vd_edge, const st
|
||||
}
|
||||
else if (point_left != point_right) //This is a parabolic edge between a point and a line.
|
||||
{
|
||||
Point p = VoronoiUtils::getSourcePoint(*(point_left ? left_cell : right_cell), segments);
|
||||
const Segment& s = VoronoiUtils::getSourceSegment(*(point_left ? right_cell : left_cell), segments);
|
||||
return VoronoiUtils::discretizeParabola(p, s, start, end, discretization_step_size, transitioning_angle);
|
||||
Point p = Geometry::VoronoiUtils::get_source_point(*(point_left ? left_cell : right_cell), segments.begin(), segments.end());
|
||||
const Segment& s = Geometry::VoronoiUtils::get_source_segment(*(point_left ? right_cell : left_cell), segments.begin(), segments.end());
|
||||
return Geometry::VoronoiUtils::discretize_parabola(p, s, start, end, discretization_step_size, transitioning_angle);
|
||||
}
|
||||
else //This is a straight edge between two points.
|
||||
{
|
||||
/*While the edge is straight, it is still discretized since the part
|
||||
becomes narrower between the two points. As such it may need different
|
||||
beadings along the way.*/
|
||||
Point left_point = VoronoiUtils::getSourcePoint(*left_cell, segments);
|
||||
Point right_point = VoronoiUtils::getSourcePoint(*right_cell, segments);
|
||||
coord_t d = (right_point - left_point).cast<int64_t>().norm();
|
||||
Point middle = (left_point + right_point) / 2;
|
||||
Point x_axis_dir = perp(Point(right_point - left_point));
|
||||
Point left_point = Geometry::VoronoiUtils::get_source_point(*left_cell, segments.begin(), segments.end());
|
||||
Point right_point = Geometry::VoronoiUtils::get_source_point(*right_cell, segments.begin(), segments.end());
|
||||
coord_t d = (right_point - left_point).cast<int64_t>().norm();
|
||||
Point middle = (left_point + right_point) / 2;
|
||||
Point x_axis_dir = perp(Point(right_point - left_point));
|
||||
coord_t x_axis_length = x_axis_dir.cast<int64_t>().norm();
|
||||
|
||||
const auto projected_x = [x_axis_dir, x_axis_length, middle](Point from) //Project a point on the edge.
|
||||
@ -345,11 +342,11 @@ bool SkeletalTrapezoidation::computePointCellRange(VD::cell_type& cell, Point& s
|
||||
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 = VoronoiUtils::getSourcePoint(cell, segments);
|
||||
const PolygonsPointIndex source_point_index = VoronoiUtils::getSourcePointIndex(cell, segments);
|
||||
Vec2i64 some_point = VoronoiUtils::p(cell.incident_edge()->vertex0());
|
||||
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 = VoronoiUtils::p(cell.incident_edge()->vertex1());
|
||||
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.
|
||||
@ -361,13 +358,13 @@ bool SkeletalTrapezoidation::computePointCellRange(VD::cell_type& cell, Point& s
|
||||
VD::edge_type* vd_edge = cell.incident_edge();
|
||||
do {
|
||||
assert(vd_edge->is_finite());
|
||||
if (Vec2i64 p1 = VoronoiUtils::p(vd_edge->vertex1()); p1 == source_point.cast<int64_t>()) {
|
||||
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((VoronoiUtils::p(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.");
|
||||
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());
|
||||
@ -376,47 +373,6 @@ bool SkeletalTrapezoidation::computePointCellRange(VD::cell_type& cell, Point& s
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkeletalTrapezoidation::computeSegmentCellRange(VD::cell_type& cell, Point& start_source_point, Point& end_source_point, VD::edge_type*& starting_vd_edge, VD::edge_type*& ending_vd_edge, const std::vector<Segment>& segments)
|
||||
{
|
||||
const 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;
|
||||
VD::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<int64_t>() && v1 == from.cast<int64_t>() ));
|
||||
if (v0 == to.cast<int64_t>() && !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<int64_t>() && (!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());
|
||||
|
||||
assert(starting_vd_edge && ending_vd_edge);
|
||||
assert(starting_vd_edge != ending_vd_edge);
|
||||
|
||||
start_source_point = source_segment.to();
|
||||
end_source_point = source_segment.from();
|
||||
}
|
||||
|
||||
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,
|
||||
@ -433,11 +389,11 @@ SkeletalTrapezoidation::SkeletalTrapezoidation(const Polygons& polys, const Bead
|
||||
|
||||
static bool has_finite_edge_with_non_finite_vertex(const VD &voronoi_diagram)
|
||||
{
|
||||
for (const VoronoiUtils::vd_t::edge_type &edge : voronoi_diagram.edges()) {
|
||||
for (const VD::edge_type &edge : voronoi_diagram.edges()) {
|
||||
if (edge.is_finite()) {
|
||||
assert(edge.vertex0() != nullptr && edge.vertex1() != nullptr);
|
||||
if (edge.vertex0() == nullptr || edge.vertex1() == nullptr || !VoronoiUtils::is_finite(*edge.vertex0()) ||
|
||||
!VoronoiUtils::is_finite(*edge.vertex1()))
|
||||
if (edge.vertex0() == nullptr || edge.vertex1() == nullptr || !Geometry::VoronoiUtils::is_finite(*edge.vertex0()) ||
|
||||
!Geometry::VoronoiUtils::is_finite(*edge.vertex1()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -453,7 +409,7 @@ static bool detect_missing_voronoi_vertex(const VD &voronoi_diagram, const std::
|
||||
continue; // There is no spoon
|
||||
|
||||
if (cell.contains_segment()) {
|
||||
const SkeletalTrapezoidation::Segment &source_segment = VoronoiUtils::getSourceSegment(cell, segments);
|
||||
const SkeletalTrapezoidation::Segment &source_segment = Geometry::VoronoiUtils::get_source_segment(cell, segments.begin(), segments.end());
|
||||
const Point from = source_segment.from();
|
||||
const Point to = source_segment.to();
|
||||
|
||||
@ -462,15 +418,15 @@ static bool detect_missing_voronoi_vertex(const VD &voronoi_diagram, const std::
|
||||
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();
|
||||
VD::edge_type *starting_vd_edge = nullptr;
|
||||
VD::edge_type *ending_vd_edge = nullptr;
|
||||
VD::edge_type *edge = cell.incident_edge();
|
||||
do {
|
||||
if (edge->is_infinite() || edge->vertex0() == nullptr || edge->vertex1() == nullptr || !VoronoiUtils::is_finite(*edge->vertex0()) || !VoronoiUtils::is_finite(*edge->vertex1()))
|
||||
if (edge->is_infinite() || edge->vertex0() == nullptr || edge->vertex1() == nullptr || !Geometry::VoronoiUtils::is_finite(*edge->vertex0()) || !Geometry::VoronoiUtils::is_finite(*edge->vertex1()))
|
||||
continue;
|
||||
|
||||
Vec2i64 v0 = VoronoiUtils::p(edge->vertex0());
|
||||
Vec2i64 v1 = VoronoiUtils::p(edge->vertex1());
|
||||
Vec2i64 v0 = Geometry::VoronoiUtils::to_point(edge->vertex0());
|
||||
Vec2i64 v1 = Geometry::VoronoiUtils::to_point(edge->vertex1());
|
||||
|
||||
assert(!(v0 == to.cast<int64_t>() && v1 == from.cast<int64_t>()));
|
||||
if (v0 == to.cast<int64_t>() && !after_start) { // Use the last edge which starts in source_segment.to
|
||||
@ -517,7 +473,7 @@ inline static void rotate_back_skeletal_trapezoidation_graph_after_fix(SkeletalT
|
||||
}
|
||||
}
|
||||
|
||||
bool detect_voronoi_edge_intersecting_input_segment(const VD &voronoi_diagram, const std::vector<VoronoiUtils::Segment> &segments)
|
||||
bool detect_voronoi_edge_intersecting_input_segment(const VD &voronoi_diagram, const std::vector<PolygonsSegmentIndex> &segments)
|
||||
{
|
||||
for (VD::cell_type cell : voronoi_diagram.cells()) {
|
||||
if (!cell.incident_edge())
|
||||
@ -526,18 +482,16 @@ bool detect_voronoi_edge_intersecting_input_segment(const VD &voronoi_diagram, c
|
||||
if (!cell.contains_segment())
|
||||
continue; // Skip cells that don't contain segments.
|
||||
|
||||
const VoronoiUtils::Segment &source_segment = VoronoiUtils::getSourceSegment(cell, segments);
|
||||
const Vec2d source_segment_from = source_segment.from().cast<double>();
|
||||
const Vec2d source_segment_vec = source_segment.to().cast<double>() - source_segment_from;
|
||||
const PolygonsSegmentIndex &source_segment = Geometry::VoronoiUtils::get_source_segment(cell, segments.begin(), segments.end());
|
||||
const Vec2d source_segment_from = source_segment.from().cast<double>();
|
||||
const Vec2d source_segment_vec = source_segment.to().cast<double>() - source_segment_from;
|
||||
|
||||
Point start_source_point, end_source_point;
|
||||
VD::edge_type *begin_voronoi_edge = nullptr, *end_voronoi_edge = nullptr;
|
||||
SkeletalTrapezoidation::computeSegmentCellRange(cell, start_source_point, end_source_point, begin_voronoi_edge, end_voronoi_edge, segments);
|
||||
Geometry::SegmentCellRange<Point> cell_range = Geometry::VoronoiUtils::compute_segment_cell_range(cell, segments.begin(), segments.end());
|
||||
// All Voronoi vertices must be on left side of the source segment, otherwise Voronoi diagram is invalid.
|
||||
// FIXME Lukas H.: Be aware that begin_voronoi_edge and end_voronoi_edge could be nullptr in some specific cases.
|
||||
// It mostly happens when there is some missing Voronoi, for example, in GH issue #8846 (IssuesWithMysteriousPerimeters.3mf).
|
||||
if (begin_voronoi_edge != nullptr && end_voronoi_edge != nullptr)
|
||||
for (VD::edge_type *edge = begin_voronoi_edge; edge != end_voronoi_edge; edge = edge->next())
|
||||
if (cell_range.edge_begin != nullptr && cell_range.edge_end != nullptr)
|
||||
for (VD::edge_type *edge = cell_range.edge_begin; edge != cell_range.edge_end; edge = edge->next())
|
||||
if (const Vec2d edge_v1(edge->vertex1()->x(), edge->vertex1()->y()); Slic3r::cross2(source_segment_vec, edge_v1 - source_segment_from) < 0)
|
||||
return true;
|
||||
}
|
||||
@ -652,7 +606,7 @@ void SkeletalTrapezoidation::constructFromPolygons(const Polygons& polys)
|
||||
#endif
|
||||
|
||||
VD voronoi_diagram;
|
||||
voronoi_diagram.construct_voronoi(segments.begin(), segments.end());
|
||||
voronoi_diagram.construct_voronoi(segments.cbegin(), segments.cend());
|
||||
|
||||
#ifdef ARACHNE_DEBUG_VORONOI
|
||||
{
|
||||
@ -696,10 +650,10 @@ process_voronoi_diagram:
|
||||
if (!cell.incident_edge())
|
||||
continue; // There is no spoon
|
||||
|
||||
Point start_source_point;
|
||||
Point end_source_point;
|
||||
VD::edge_type* starting_voronoi_edge = nullptr;
|
||||
VD::edge_type* ending_voronoi_edge = nullptr;
|
||||
Point start_source_point;
|
||||
Point end_source_point;
|
||||
VD::edge_type *starting_voronoi_edge = nullptr;
|
||||
VD::edge_type *ending_voronoi_edge = nullptr;
|
||||
// Compute and store result in above variables
|
||||
|
||||
if (cell.contains_point()) {
|
||||
@ -708,7 +662,12 @@ process_voronoi_diagram:
|
||||
continue;
|
||||
} else {
|
||||
assert(cell.contains_segment());
|
||||
computeSegmentCellRange(cell, start_source_point, end_source_point, starting_voronoi_edge, ending_voronoi_edge, segments);
|
||||
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;
|
||||
starting_voronoi_edge = cell_range.edge_begin;
|
||||
ending_voronoi_edge = cell_range.edge_end;
|
||||
}
|
||||
|
||||
if (!starting_voronoi_edge || !ending_voronoi_edge) {
|
||||
@ -717,33 +676,25 @@ process_voronoi_diagram:
|
||||
}
|
||||
|
||||
// Copy start to end edge to graph
|
||||
edge_t* prev_edge = nullptr;
|
||||
assert(VoronoiUtils::p(starting_voronoi_edge->vertex1()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex1()).x() >= std::numeric_limits<coord_t>::lowest());
|
||||
assert(VoronoiUtils::p(starting_voronoi_edge->vertex1()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex1()).y() >= std::numeric_limits<coord_t>::lowest());
|
||||
transferEdge(start_source_point, VoronoiUtils::p(starting_voronoi_edge->vertex1()).cast<coord_t>(), *starting_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
|
||||
node_t* starting_node = vd_node_to_he_node[starting_voronoi_edge->vertex0()];
|
||||
assert(Geometry::VoronoiUtils::is_in_range<coord_t>(*starting_voronoi_edge));
|
||||
edge_t *prev_edge = nullptr;
|
||||
transferEdge(start_source_point, Geometry::VoronoiUtils::to_point(starting_voronoi_edge->vertex1()).cast<coord_t>(), *starting_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
|
||||
node_t *starting_node = vd_node_to_he_node[starting_voronoi_edge->vertex0()];
|
||||
starting_node->data.distance_to_boundary = 0;
|
||||
|
||||
constexpr bool is_next_to_start_or_end = true;
|
||||
graph.makeRib(prev_edge, start_source_point, end_source_point, is_next_to_start_or_end);
|
||||
for (VD::edge_type* vd_edge = starting_voronoi_edge->next(); vd_edge != ending_voronoi_edge; vd_edge = vd_edge->next()) {
|
||||
assert(vd_edge->is_finite());
|
||||
assert(Geometry::VoronoiUtils::is_in_range<coord_t>(*vd_edge));
|
||||
|
||||
assert(VoronoiUtils::p(vd_edge->vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
|
||||
assert(VoronoiUtils::p(vd_edge->vertex0()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex0()).y() >= std::numeric_limits<coord_t>::lowest());
|
||||
assert(VoronoiUtils::p(vd_edge->vertex1()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex1()).x() >= std::numeric_limits<coord_t>::lowest());
|
||||
assert(VoronoiUtils::p(vd_edge->vertex1()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(vd_edge->vertex1()).y() >= std::numeric_limits<coord_t>::lowest());
|
||||
|
||||
Point v1 = VoronoiUtils::p(vd_edge->vertex0()).cast<coord_t>();
|
||||
Point v2 = VoronoiUtils::p(vd_edge->vertex1()).cast<coord_t>();
|
||||
Point v1 = Geometry::VoronoiUtils::to_point(vd_edge->vertex0()).cast<coord_t>();
|
||||
Point v2 = Geometry::VoronoiUtils::to_point(vd_edge->vertex1()).cast<coord_t>();
|
||||
transferEdge(v1, v2, *vd_edge, prev_edge, start_source_point, end_source_point, segments);
|
||||
|
||||
graph.makeRib(prev_edge, start_source_point, end_source_point, vd_edge->next() == ending_voronoi_edge);
|
||||
}
|
||||
|
||||
assert(VoronoiUtils::p(starting_voronoi_edge->vertex0()).x() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex0()).x() >= std::numeric_limits<coord_t>::lowest());
|
||||
assert(VoronoiUtils::p(starting_voronoi_edge->vertex0()).y() <= std::numeric_limits<coord_t>::max() && VoronoiUtils::p(starting_voronoi_edge->vertex0()).y() >= std::numeric_limits<coord_t>::lowest());
|
||||
transferEdge(VoronoiUtils::p(ending_voronoi_edge->vertex0()).cast<coord_t>(), end_source_point, *ending_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
|
||||
transferEdge(Geometry::VoronoiUtils::to_point(ending_voronoi_edge->vertex0()).cast<coord_t>(), end_source_point, *ending_voronoi_edge, prev_edge, start_source_point, end_source_point, segments);
|
||||
prev_edge->to->data.distance_to_boundary = 0;
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,6 @@
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include <Arachne/utils/VoronoiUtils.hpp>
|
||||
|
||||
#include "utils/HalfEdgeGraph.hpp"
|
||||
#include "utils/PolygonsSegmentIndex.hpp"
|
||||
#include "utils/ExtrusionJunction.hpp"
|
||||
@ -235,32 +233,6 @@ protected:
|
||||
*/
|
||||
static bool computePointCellRange(VD::cell_type& cell, Point& start_source_point, Point& end_source_point, VD::edge_type*& starting_vd_edge, VD::edge_type*& ending_vd_edge, const std::vector<Segment>& segments);
|
||||
|
||||
/*!
|
||||
* Compute the range of line segments that surround a cell of the skeletal
|
||||
* graph that belongs to a line segment of the medial axis.
|
||||
*
|
||||
* This should only be used on cells that belong to a central line segment
|
||||
* of the skeletal graph, e.g. trapezoid cells, not triangular 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 void computeSegmentCellRange(VD::cell_type& cell, Point& start_source_point, Point& end_source_point, VD::edge_type*& starting_vd_edge, 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
|
||||
@ -602,7 +574,7 @@ protected:
|
||||
*/
|
||||
void generateLocalMaximaSingleBeads();
|
||||
|
||||
friend bool detect_voronoi_edge_intersecting_input_segment(const VD &voronoi_diagram, const std::vector<VoronoiUtils::Segment> &segments);
|
||||
friend bool detect_voronoi_edge_intersecting_input_segment(const VD &voronoi_diagram, const std::vector<Segment> &segments);
|
||||
};
|
||||
|
||||
} // namespace Slic3r::Arachne
|
||||
|
@ -1,181 +0,0 @@
|
||||
//Copyright (c) 2021 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
#include <stack>
|
||||
#include <optional>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "linearAlg2D.hpp"
|
||||
#include "VoronoiUtils.hpp"
|
||||
#include "libslic3r/Geometry/VoronoiUtils.hpp"
|
||||
|
||||
namespace Slic3r::Arachne
|
||||
{
|
||||
|
||||
Vec2i64 VoronoiUtils::p(const vd_t::vertex_type *node)
|
||||
{
|
||||
const double x = node->x();
|
||||
const double y = node->y();
|
||||
assert(std::isfinite(x) && std::isfinite(y));
|
||||
assert(x <= double(std::numeric_limits<int64_t>::max()) && x >= std::numeric_limits<int64_t>::lowest());
|
||||
assert(y <= double(std::numeric_limits<int64_t>::max()) && y >= std::numeric_limits<int64_t>::lowest());
|
||||
return {int64_t(x + 0.5 - (x < 0)), int64_t(y + 0.5 - (y < 0))}; // Round to the nearest integer coordinates.
|
||||
}
|
||||
|
||||
Point VoronoiUtils::getSourcePoint(const vd_t::cell_type& cell, const std::vector<Segment>& segments)
|
||||
{
|
||||
assert(cell.contains_point());
|
||||
if(!cell.contains_point())
|
||||
BOOST_LOG_TRIVIAL(debug) << "Voronoi cell doesn't contain a source point!";
|
||||
|
||||
switch (cell.source_category()) {
|
||||
case boost::polygon::SOURCE_CATEGORY_SINGLE_POINT:
|
||||
assert(false && "Voronoi diagram is always constructed using segments, so cell.source_category() shouldn't be SOURCE_CATEGORY_SINGLE_POINT!\n");
|
||||
BOOST_LOG_TRIVIAL(error) << "Voronoi diagram is always constructed using segments, so cell.source_category() shouldn't be SOURCE_CATEGORY_SINGLE_POINT!";
|
||||
break;
|
||||
case boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT:
|
||||
assert(cell.source_index() < segments.size());
|
||||
return boost::polygon::segment_traits<Segment>::get(segments[cell.source_index()], boost::polygon::LOW);
|
||||
break;
|
||||
case boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT:
|
||||
assert(cell.source_index() < segments.size());
|
||||
return boost::polygon::segment_traits<Segment>::get(segments[cell.source_index()], boost::polygon::HIGH);
|
||||
break;
|
||||
default:
|
||||
assert(false && "getSourcePoint should only be called on point cells!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
assert(false && "cell.source_category() is equal to an invalid value!\n");
|
||||
BOOST_LOG_TRIVIAL(error) << "cell.source_category() is equal to an invalid value!";
|
||||
return {};
|
||||
}
|
||||
|
||||
PolygonsPointIndex VoronoiUtils::getSourcePointIndex(const vd_t::cell_type& cell, const std::vector<Segment>& segments)
|
||||
{
|
||||
assert(cell.contains_point());
|
||||
if(!cell.contains_point())
|
||||
BOOST_LOG_TRIVIAL(debug) << "Voronoi cell doesn't contain a source point!";
|
||||
|
||||
assert(cell.source_category() != boost::polygon::SOURCE_CATEGORY_SINGLE_POINT);
|
||||
switch (cell.source_category()) {
|
||||
case boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT: {
|
||||
assert(cell.source_index() < segments.size());
|
||||
return segments[cell.source_index()];
|
||||
break;
|
||||
}
|
||||
case boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT: {
|
||||
assert(cell.source_index() < segments.size());
|
||||
return segments[cell.source_index()].next();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false && "getSourcePoint should only be called on point cells!\n");
|
||||
break;
|
||||
}
|
||||
PolygonsPointIndex ret = segments[cell.source_index()];
|
||||
return ++ret;
|
||||
}
|
||||
|
||||
const VoronoiUtils::Segment &VoronoiUtils::getSourceSegment(const vd_t::cell_type &cell, const std::vector<Segment> &segments)
|
||||
{
|
||||
assert(cell.contains_segment());
|
||||
if (!cell.contains_segment())
|
||||
BOOST_LOG_TRIVIAL(debug) << "Voronoi cell doesn't contain a source segment!";
|
||||
|
||||
return segments[cell.source_index()];
|
||||
}
|
||||
|
||||
Points VoronoiUtils::discretizeParabola(const Point &source_point, const Segment &source_segment, const Point &start, const Point &end, const coord_t approximate_step_size, float transitioning_angle)
|
||||
{
|
||||
Points discretized;
|
||||
// x is distance of point projected on the segment ab
|
||||
// xx is point projected on the segment ab
|
||||
const Point a = source_segment.from();
|
||||
const Point b = source_segment.to();
|
||||
const Point ab = b - a;
|
||||
const Point as = start - a;
|
||||
const Point ae = end - a;
|
||||
const coord_t ab_size = ab.cast<int64_t>().norm();
|
||||
const coord_t sx = as.cast<int64_t>().dot(ab.cast<int64_t>()) / ab_size;
|
||||
const coord_t ex = ae.cast<int64_t>().dot(ab.cast<int64_t>()) / ab_size;
|
||||
const coord_t sxex = ex - sx;
|
||||
|
||||
const Point ap = source_point - a;
|
||||
const coord_t px = ap.cast<int64_t>().dot(ab.cast<int64_t>()) / ab_size;
|
||||
|
||||
Point pxx;
|
||||
Line(a, b).distance_to_infinite_squared(source_point, &pxx);
|
||||
const Point ppxx = pxx - source_point;
|
||||
const coord_t d = ppxx.cast<int64_t>().norm();
|
||||
|
||||
const Vec2d rot = perp(ppxx).cast<double>().normalized();
|
||||
const double rot_cos_theta = rot.x();
|
||||
const double rot_sin_theta = rot.y();
|
||||
|
||||
if (d == 0) {
|
||||
discretized.emplace_back(start);
|
||||
discretized.emplace_back(end);
|
||||
return discretized;
|
||||
}
|
||||
|
||||
const double marking_bound = atan(transitioning_angle * 0.5);
|
||||
int64_t msx = -marking_bound * int64_t(d); // projected marking_start
|
||||
int64_t mex = marking_bound * int64_t(d); // projected marking_end
|
||||
|
||||
const coord_t marking_start_end_h = msx * msx / (2 * d) + d / 2;
|
||||
Point marking_start = Point(coord_t(msx), marking_start_end_h).rotated(rot_cos_theta, rot_sin_theta) + pxx;
|
||||
Point marking_end = Point(coord_t(mex), marking_start_end_h).rotated(rot_cos_theta, rot_sin_theta) + pxx;
|
||||
const int dir = (sx > ex) ? -1 : 1;
|
||||
if (dir < 0) {
|
||||
std::swap(marking_start, marking_end);
|
||||
std::swap(msx, mex);
|
||||
}
|
||||
|
||||
bool add_marking_start = msx * int64_t(dir) > int64_t(sx - px) * int64_t(dir) && msx * int64_t(dir) < int64_t(ex - px) * int64_t(dir);
|
||||
bool add_marking_end = mex * int64_t(dir) > int64_t(sx - px) * int64_t(dir) && mex * int64_t(dir) < int64_t(ex - px) * int64_t(dir);
|
||||
|
||||
const Point apex = Point(0, d / 2).rotated(rot_cos_theta, rot_sin_theta) + pxx;
|
||||
bool add_apex = int64_t(sx - px) * int64_t(dir) < 0 && int64_t(ex - px) * int64_t(dir) > 0;
|
||||
|
||||
assert(!add_marking_start || !add_marking_end || add_apex);
|
||||
if (add_marking_start && add_marking_end && !add_apex)
|
||||
BOOST_LOG_TRIVIAL(warning) << "Failing to discretize parabola! Must add an apex or one of the endpoints.";
|
||||
|
||||
const coord_t step_count = lround(static_cast<double>(std::abs(ex - sx)) / approximate_step_size);
|
||||
discretized.emplace_back(start);
|
||||
for (coord_t step = 1; step < step_count; ++step) {
|
||||
const int64_t x = int64_t(sx) + int64_t(sxex) * int64_t(step) / int64_t(step_count) - int64_t(px);
|
||||
const int64_t y = int64_t(x) * int64_t(x) / int64_t(2 * d) + int64_t(d / 2);
|
||||
|
||||
if (add_marking_start && msx * int64_t(dir) < int64_t(x) * int64_t(dir)) {
|
||||
discretized.emplace_back(marking_start);
|
||||
add_marking_start = false;
|
||||
}
|
||||
|
||||
if (add_apex && int64_t(x) * int64_t(dir) > 0) {
|
||||
discretized.emplace_back(apex);
|
||||
add_apex = false; // only add the apex just before the
|
||||
}
|
||||
|
||||
if (add_marking_end && mex * int64_t(dir) < int64_t(x) * int64_t(dir)) {
|
||||
discretized.emplace_back(marking_end);
|
||||
add_marking_end = false;
|
||||
}
|
||||
|
||||
assert(Geometry::VoronoiUtils::is_in_range<coord_t>(x) && Geometry::VoronoiUtils::is_in_range<coord_t>(y));
|
||||
const Point result = Point(x, y).rotated(rot_cos_theta, rot_sin_theta) + pxx;
|
||||
discretized.emplace_back(result);
|
||||
}
|
||||
|
||||
if (add_apex)
|
||||
discretized.emplace_back(apex);
|
||||
|
||||
if (add_marking_end)
|
||||
discretized.emplace_back(marking_end);
|
||||
|
||||
discretized.emplace_back(end);
|
||||
return discretized;
|
||||
}
|
||||
|
||||
}//namespace Slic3r::Arachne
|
@ -1,47 +0,0 @@
|
||||
//Copyright (c) 2020 Ultimaker B.V.
|
||||
//CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
|
||||
#ifndef UTILS_VORONOI_UTILS_H
|
||||
#define UTILS_VORONOI_UTILS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
#include <boost/polygon/voronoi.hpp>
|
||||
|
||||
#include "PolygonsSegmentIndex.hpp"
|
||||
|
||||
namespace Slic3r::Arachne
|
||||
{
|
||||
|
||||
/*!
|
||||
*/
|
||||
class VoronoiUtils
|
||||
{
|
||||
public:
|
||||
using Segment = PolygonsSegmentIndex;
|
||||
using voronoi_data_t = double;
|
||||
using vd_t = boost::polygon::voronoi_diagram<voronoi_data_t>;
|
||||
|
||||
static Point getSourcePoint(const vd_t::cell_type &cell, const std::vector<Segment> &segments);
|
||||
static const Segment &getSourceSegment(const vd_t::cell_type &cell, const std::vector<Segment> &segments);
|
||||
static PolygonsPointIndex getSourcePointIndex(const vd_t::cell_type &cell, const std::vector<Segment> &segments);
|
||||
|
||||
static Vec2i64 p(const vd_t::vertex_type *node);
|
||||
|
||||
/*!
|
||||
* Discretize a parabola based on (approximate) step size.
|
||||
* The \p approximate_step_size is measured parallel to the \p source_segment, not along the parabola.
|
||||
*/
|
||||
static Points discretizeParabola(const Point &source_point, const Segment &source_segment, const Point& start, const Point &end, coord_t approximate_step_size, float transitioning_angle);
|
||||
|
||||
static inline bool is_finite(const VoronoiUtils::vd_t::vertex_type &vertex)
|
||||
{
|
||||
return std::isfinite(vertex.x()) && std::isfinite(vertex.y());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Slic3r::Arachne
|
||||
|
||||
#endif // UTILS_VORONOI_UTILS_H
|
@ -496,8 +496,6 @@ set(SLIC3R_SOURCES
|
||||
Arachne/utils/PolygonsSegmentIndex.hpp
|
||||
Arachne/utils/PolylineStitcher.hpp
|
||||
Arachne/utils/PolylineStitcher.cpp
|
||||
Arachne/utils/VoronoiUtils.hpp
|
||||
Arachne/utils/VoronoiUtils.cpp
|
||||
Arachne/SkeletalTrapezoidation.hpp
|
||||
Arachne/SkeletalTrapezoidation.cpp
|
||||
Arachne/SkeletalTrapezoidationEdge.hpp
|
||||
|
@ -1,7 +1,251 @@
|
||||
#include <Arachne/utils/PolygonsSegmentIndex.hpp>
|
||||
#include <MultiMaterialSegmentation.hpp>
|
||||
|
||||
#include "VoronoiUtils.hpp"
|
||||
|
||||
namespace Slic3r::Geometry {
|
||||
|
||||
using PolygonsSegmentIndexConstIt = std::vector<Arachne::PolygonsSegmentIndex>::const_iterator;
|
||||
using LinesIt = Lines::iterator;
|
||||
using ColoredLinesIt = ColoredLines::iterator;
|
||||
|
||||
// Explicit template instantiation.
|
||||
template LinesIt::reference VoronoiUtils::get_source_segment(const VoronoiDiagram::cell_type &, LinesIt, LinesIt);
|
||||
template ColoredLinesIt::reference VoronoiUtils::get_source_segment(const VoronoiDiagram::cell_type &, ColoredLinesIt, ColoredLinesIt);
|
||||
template PolygonsSegmentIndexConstIt::reference VoronoiUtils::get_source_segment(const VoronoiDiagram::cell_type &, PolygonsSegmentIndexConstIt, PolygonsSegmentIndexConstIt);
|
||||
template Point VoronoiUtils::get_source_point(const VoronoiDiagram::cell_type &, LinesIt, LinesIt);
|
||||
template Point VoronoiUtils::get_source_point(const VoronoiDiagram::cell_type &, ColoredLinesIt, ColoredLinesIt);
|
||||
template Point VoronoiUtils::get_source_point(const VoronoiDiagram::cell_type &, PolygonsSegmentIndexConstIt, PolygonsSegmentIndexConstIt);
|
||||
template SegmentCellRange<Point> VoronoiUtils::compute_segment_cell_range(VoronoiDiagram::cell_type &, LinesIt, LinesIt);
|
||||
template SegmentCellRange<Point> VoronoiUtils::compute_segment_cell_range(VoronoiDiagram::cell_type &, ColoredLinesIt, ColoredLinesIt);
|
||||
template SegmentCellRange<Point> VoronoiUtils::compute_segment_cell_range(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);
|
||||
|
||||
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,
|
||||
typename std::iterator_traits<SegmentIterator>::reference>::type
|
||||
VoronoiUtils::get_source_segment(const VoronoiDiagram::cell_type &cell, const SegmentIterator segment_begin, const SegmentIterator segment_end)
|
||||
{
|
||||
if (!cell.contains_segment())
|
||||
throw Slic3r::InvalidArgument("Voronoi cell doesn't contain a source segment!");
|
||||
|
||||
if (cell.source_index() >= size_t(std::distance(segment_begin, segment_end)))
|
||||
throw Slic3r::OutOfRange("Voronoi cell source index is out of range!");
|
||||
|
||||
return *(segment_begin + cell.source_index());
|
||||
}
|
||||
|
||||
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,
|
||||
typename boost::polygon::segment_point_type<typename std::iterator_traits<SegmentIterator>::value_type>::type>::type
|
||||
VoronoiUtils::get_source_point(const VoronoiDiagram::cell_type &cell, const SegmentIterator segment_begin, const SegmentIterator segment_end)
|
||||
{
|
||||
using Segment = typename std::iterator_traits<SegmentIterator>::value_type;
|
||||
|
||||
if (!cell.contains_point())
|
||||
throw Slic3r::InvalidArgument("Voronoi cell doesn't contain a source point!");
|
||||
|
||||
if (cell.source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) {
|
||||
assert(int(cell.source_index()) < std::distance(segment_begin, segment_end));
|
||||
const SegmentIterator segment_it = segment_begin + cell.source_index();
|
||||
return boost::polygon::segment_traits<Segment>::get(*segment_it, boost::polygon::LOW);
|
||||
} else if (cell.source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT) {
|
||||
assert(int(cell.source_index()) < std::distance(segment_begin, segment_end));
|
||||
const SegmentIterator segment_it = segment_begin + cell.source_index();
|
||||
return boost::polygon::segment_traits<Segment>::get(*segment_it, boost::polygon::HIGH);
|
||||
} else if (cell.source_category() == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT) {
|
||||
throw Slic3r::RuntimeError("Voronoi diagram is always constructed using segments, so cell.source_category() shouldn't be SOURCE_CATEGORY_SINGLE_POINT!");
|
||||
} else {
|
||||
throw Slic3r::InvalidArgument("Function get_source_point() should only be called on point cells!");
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
Arachne::PolygonsPointIndex>::type
|
||||
VoronoiUtils::get_source_point_index(const VD::cell_type &cell, const SegmentIterator segment_begin, const SegmentIterator segment_end)
|
||||
{
|
||||
if (!cell.contains_point())
|
||||
throw Slic3r::InvalidArgument("Voronoi cell doesn't contain a source point!");
|
||||
|
||||
if (cell.source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_START_POINT) {
|
||||
assert(int(cell.source_index()) < std::distance(segment_begin, segment_end));
|
||||
const SegmentIterator segment_it = segment_begin + cell.source_index();
|
||||
return (*segment_it);
|
||||
} else if (cell.source_category() == boost::polygon::SOURCE_CATEGORY_SEGMENT_END_POINT) {
|
||||
assert(int(cell.source_index()) < std::distance(segment_begin, segment_end));
|
||||
const SegmentIterator segment_it = segment_begin + cell.source_index();
|
||||
return (*segment_it).next();
|
||||
} else if (cell.source_category() == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT) {
|
||||
throw Slic3r::RuntimeError("Voronoi diagram is always constructed using segments, so cell.source_category() shouldn't be SOURCE_CATEGORY_SINGLE_POINT!");
|
||||
} else {
|
||||
throw Slic3r::InvalidArgument("Function get_source_point_index() should only be called on point cells!");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Segment>
|
||||
typename boost::polygon::enable_if<typename boost::polygon::gtl_if<typename boost::polygon::is_segment_concept<
|
||||
typename boost::polygon::geometry_concept<Segment>::type>::type>::type,
|
||||
Points>::type
|
||||
VoronoiUtils::discretize_parabola(const Point &source_point, const Segment &source_segment, const Point &start, const Point &end, const coord_t approximate_step_size, float transitioning_angle)
|
||||
{
|
||||
Points discretized;
|
||||
// x is distance of point projected on the segment ab
|
||||
// xx is point projected on the segment ab
|
||||
const Point a = source_segment.from();
|
||||
const Point b = source_segment.to();
|
||||
const Point ab = b - a;
|
||||
const Point as = start - a;
|
||||
const Point ae = end - a;
|
||||
const coord_t ab_size = ab.cast<int64_t>().norm();
|
||||
const coord_t sx = as.cast<int64_t>().dot(ab.cast<int64_t>()) / ab_size;
|
||||
const coord_t ex = ae.cast<int64_t>().dot(ab.cast<int64_t>()) / ab_size;
|
||||
const coord_t sxex = ex - sx;
|
||||
|
||||
const Point ap = source_point - a;
|
||||
const coord_t px = ap.cast<int64_t>().dot(ab.cast<int64_t>()) / ab_size;
|
||||
|
||||
Point pxx;
|
||||
Line(a, b).distance_to_infinite_squared(source_point, &pxx);
|
||||
const Point ppxx = pxx - source_point;
|
||||
const coord_t d = ppxx.cast<int64_t>().norm();
|
||||
|
||||
const Vec2d rot = perp(ppxx).cast<double>().normalized();
|
||||
const double rot_cos_theta = rot.x();
|
||||
const double rot_sin_theta = rot.y();
|
||||
|
||||
if (d == 0) {
|
||||
discretized.emplace_back(start);
|
||||
discretized.emplace_back(end);
|
||||
return discretized;
|
||||
}
|
||||
|
||||
const double marking_bound = atan(transitioning_angle * 0.5);
|
||||
int64_t msx = -marking_bound * int64_t(d); // projected marking_start
|
||||
int64_t mex = marking_bound * int64_t(d); // projected marking_end
|
||||
|
||||
const coord_t marking_start_end_h = msx * msx / (2 * d) + d / 2;
|
||||
Point marking_start = Point(coord_t(msx), marking_start_end_h).rotated(rot_cos_theta, rot_sin_theta) + pxx;
|
||||
Point marking_end = Point(coord_t(mex), marking_start_end_h).rotated(rot_cos_theta, rot_sin_theta) + pxx;
|
||||
const int dir = (sx > ex) ? -1 : 1;
|
||||
if (dir < 0) {
|
||||
std::swap(marking_start, marking_end);
|
||||
std::swap(msx, mex);
|
||||
}
|
||||
|
||||
bool add_marking_start = msx * int64_t(dir) > int64_t(sx - px) * int64_t(dir) && msx * int64_t(dir) < int64_t(ex - px) * int64_t(dir);
|
||||
bool add_marking_end = mex * int64_t(dir) > int64_t(sx - px) * int64_t(dir) && mex * int64_t(dir) < int64_t(ex - px) * int64_t(dir);
|
||||
|
||||
const Point apex = Point(0, d / 2).rotated(rot_cos_theta, rot_sin_theta) + pxx;
|
||||
bool add_apex = int64_t(sx - px) * int64_t(dir) < 0 && int64_t(ex - px) * int64_t(dir) > 0;
|
||||
|
||||
assert(!add_marking_start || !add_marking_end || add_apex);
|
||||
if (add_marking_start && add_marking_end && !add_apex)
|
||||
BOOST_LOG_TRIVIAL(warning) << "Failing to discretize parabola! Must add an apex or one of the endpoints.";
|
||||
|
||||
const coord_t step_count = lround(static_cast<double>(std::abs(ex - sx)) / approximate_step_size);
|
||||
discretized.emplace_back(start);
|
||||
for (coord_t step = 1; step < step_count; ++step) {
|
||||
const int64_t x = int64_t(sx) + int64_t(sxex) * int64_t(step) / int64_t(step_count) - int64_t(px);
|
||||
const int64_t y = int64_t(x) * int64_t(x) / int64_t(2 * d) + int64_t(d / 2);
|
||||
|
||||
if (add_marking_start && msx * int64_t(dir) < int64_t(x) * int64_t(dir)) {
|
||||
discretized.emplace_back(marking_start);
|
||||
add_marking_start = false;
|
||||
}
|
||||
|
||||
if (add_apex && int64_t(x) * int64_t(dir) > 0) {
|
||||
discretized.emplace_back(apex);
|
||||
add_apex = false; // only add the apex just before the
|
||||
}
|
||||
|
||||
if (add_marking_end && mex * int64_t(dir) < int64_t(x) * int64_t(dir)) {
|
||||
discretized.emplace_back(marking_end);
|
||||
add_marking_end = false;
|
||||
}
|
||||
|
||||
assert(is_in_range<coord_t>(x) && is_in_range<coord_t>(y));
|
||||
const Point result = Point(x, y).rotated(rot_cos_theta, rot_sin_theta) + pxx;
|
||||
discretized.emplace_back(result);
|
||||
}
|
||||
|
||||
if (add_apex)
|
||||
discretized.emplace_back(apex);
|
||||
|
||||
if (add_marking_end)
|
||||
discretized.emplace_back(marking_end);
|
||||
|
||||
discretized.emplace_back(end);
|
||||
return discretized;
|
||||
}
|
||||
|
||||
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::SegmentCellRange<
|
||||
typename boost::polygon::segment_point_type<typename std::iterator_traits<SegmentIterator>::value_type>::type>>::type
|
||||
VoronoiUtils::compute_segment_cell_range(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 SegmentCellRange = SegmentCellRange<Point>;
|
||||
|
||||
const Segment &source_segment = Geometry::VoronoiUtils::get_source_segment(cell, segment_begin, segment_end);
|
||||
const Point from = boost::polygon::segment_traits<Segment>::get(source_segment, boost::polygon::LOW);
|
||||
const Point to = boost::polygon::segment_traits<Segment>::get(source_segment, boost::polygon::HIGH);
|
||||
const Vec2i64 from_i64 = from.template cast<int64_t>();
|
||||
const Vec2i64 to_i64 = to.template cast<int64_t>();
|
||||
|
||||
// FIXME @hejllukas: Ensure that there is no infinite edge during iteration between edge_begin and edge_end.
|
||||
SegmentCellRange cell_range(to, from);
|
||||
|
||||
// Find starting edge and end edge
|
||||
bool seen_possible_start = false;
|
||||
bool after_start = false;
|
||||
bool ending_edge_is_set_before_start = false;
|
||||
VD::edge_type *edge = cell.incident_edge();
|
||||
do {
|
||||
if (edge->is_infinite())
|
||||
continue;
|
||||
|
||||
Vec2i64 v0 = Geometry::VoronoiUtils::to_point(edge->vertex0());
|
||||
Vec2i64 v1 = Geometry::VoronoiUtils::to_point(edge->vertex1());
|
||||
assert(v0 != to_i64 || v1 != from_i64);
|
||||
|
||||
if (v0 == to_i64 && !after_start) { // Use the last edge which starts in source_segment.to
|
||||
cell_range.edge_begin = edge;
|
||||
seen_possible_start = true;
|
||||
} else if (seen_possible_start) {
|
||||
after_start = true;
|
||||
}
|
||||
|
||||
if (v1 == from_i64 && (!cell_range.edge_end || ending_edge_is_set_before_start)) {
|
||||
ending_edge_is_set_before_start = !after_start;
|
||||
cell_range.edge_end = edge;
|
||||
}
|
||||
} while (edge = edge->next(), edge != cell.incident_edge());
|
||||
|
||||
return cell_range;
|
||||
}
|
||||
|
||||
Vec2i64 VoronoiUtils::to_point(const VD::vertex_type *vertex)
|
||||
{
|
||||
const double x = vertex->x(), y = vertex->y();
|
||||
|
||||
assert(std::isfinite(x) && std::isfinite(y));
|
||||
assert(is_in_range<int64_t>(x) && is_in_range<int64_t>(y));
|
||||
|
||||
return {std::llround(x), std::llround(y)};
|
||||
}
|
||||
|
||||
bool VoronoiUtils::is_finite(const VD::vertex_type &vertex)
|
||||
{
|
||||
return std::isfinite(vertex.x()) && std::isfinite(vertex.y());
|
||||
|
@ -2,16 +2,96 @@
|
||||
#define slic3r_VoronoiUtils_hpp_
|
||||
|
||||
#include "libslic3r/Geometry/Voronoi.hpp"
|
||||
#include "libslic3r/Arachne/utils/PolygonsSegmentIndex.hpp"
|
||||
|
||||
using VD = Slic3r::Geometry::VoronoiDiagram;
|
||||
|
||||
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.
|
||||
VD::edge_type *edge_begin = nullptr; // The edge of the Voronoi diagram where the loop around the cell starts.
|
||||
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)
|
||||
{}
|
||||
|
||||
bool is_valid() const { return edge_begin && edge_end && edge_begin != edge_end; }
|
||||
};
|
||||
|
||||
class VoronoiUtils
|
||||
{
|
||||
public:
|
||||
static Vec2i64 to_point(const VD::vertex_type *vertex);
|
||||
|
||||
static bool is_finite(const VD::vertex_type &vertex);
|
||||
|
||||
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,
|
||||
typename std::iterator_traits<SegmentIterator>::reference>::type
|
||||
get_source_segment(const VD::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end);
|
||||
|
||||
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,
|
||||
typename boost::polygon::segment_point_type<typename std::iterator_traits<SegmentIterator>::value_type>::type>::type
|
||||
get_source_point(const VoronoiDiagram::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end);
|
||||
|
||||
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,
|
||||
Arachne::PolygonsPointIndex>::type
|
||||
get_source_point_index(const VD::cell_type &cell, SegmentIterator segment_begin, SegmentIterator segment_end);
|
||||
|
||||
/**
|
||||
* Discretize a parabola based on (approximate) step size.
|
||||
*
|
||||
* Adapted from CuraEngine VoronoiUtils::discretizeParabola by Tim Kuipers @BagelOrb and @Ghostkeeper.
|
||||
*
|
||||
* @param approximate_step_size is measured parallel to the source_segment, not along the parabola.
|
||||
*/
|
||||
template<typename Segment>
|
||||
static typename boost::polygon::enable_if<typename boost::polygon::gtl_if<typename boost::polygon::is_segment_concept<
|
||||
typename boost::polygon::geometry_concept<Segment>::type>::type>::type,
|
||||
Points>::type
|
||||
discretize_parabola(const Point &source_point, const Segment &source_segment, const Point &start, const Point &end, coord_t approximate_step_size, float transitioning_angle);
|
||||
|
||||
/**
|
||||
* Compute the range of line segments that surround a cell of the skeletal
|
||||
* graph that belongs to a line segment of the medial axis.
|
||||
*
|
||||
* This should only be used on cells that belong to a central line segment
|
||||
* of the skeletal graph, e.g. trapezoid cells, not triangular 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::SegmentCellRange<
|
||||
typename boost::polygon::segment_point_type<typename std::iterator_traits<SegmentIterator>::value_type>::type>>::type
|
||||
compute_segment_cell_range(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());
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <CGAL/Surface_sweep_2_algorithms.h>
|
||||
|
||||
#include "libslic3r/Geometry/Voronoi.hpp"
|
||||
#include "libslic3r/Arachne/utils/VoronoiUtils.hpp"
|
||||
#include "libslic3r/Geometry/VoronoiUtils.hpp"
|
||||
|
||||
#include "VoronoiUtilsCgal.hpp"
|
||||
|
||||
@ -178,22 +178,22 @@ struct ParabolicSegment
|
||||
const CGAL::Orientation is_focus_on_left;
|
||||
};
|
||||
|
||||
inline static ParabolicSegment get_parabolic_segment(const VD::edge_type &edge, const std::vector<VoronoiUtils::Segment> &segments)
|
||||
inline static ParabolicSegment get_parabolic_segment(const VD::edge_type &edge, const std::vector<PolygonsSegmentIndex> &segments)
|
||||
{
|
||||
assert(edge.is_curved());
|
||||
|
||||
const VD::cell_type *left_cell = edge.cell();
|
||||
const VD::cell_type *right_cell = edge.twin()->cell();
|
||||
|
||||
const Point focus_pt = VoronoiUtils::getSourcePoint(*(left_cell->contains_point() ? left_cell : right_cell), segments);
|
||||
const VoronoiUtils::Segment &directrix = VoronoiUtils::getSourceSegment(*(left_cell->contains_point() ? right_cell : left_cell), segments);
|
||||
CGAL::Orientation focus_side = CGAL::opposite(CGAL::orientation(to_cgal_point(edge.vertex0()), to_cgal_point(edge.vertex1()), to_cgal_point(focus_pt)));
|
||||
const Point focus_pt = VoronoiUtils::get_source_point(*(left_cell->contains_point() ? left_cell : right_cell), segments.begin(), segments.end());
|
||||
const PolygonsSegmentIndex &directrix = VoronoiUtils::get_source_segment(*(left_cell->contains_point() ? right_cell : left_cell), segments.begin(), segments.end());
|
||||
CGAL::Orientation focus_side = CGAL::opposite(CGAL::orientation(to_cgal_point(edge.vertex0()), to_cgal_point(edge.vertex1()), to_cgal_point(focus_pt)));
|
||||
|
||||
assert(focus_side == CGAL::Orientation::LEFT_TURN || focus_side == CGAL::Orientation::RIGHT_TURN);
|
||||
return {focus_pt, Line(directrix.from(), directrix.to()), make_linef(edge), focus_side};
|
||||
}
|
||||
|
||||
inline static CGAL::Orientation orientation_of_two_edges(const VD::edge_type &edge_a, const VD::edge_type &edge_b, const std::vector<VoronoiUtils::Segment> &segments) {
|
||||
inline static CGAL::Orientation orientation_of_two_edges(const VD::edge_type &edge_a, const VD::edge_type &edge_b, const std::vector<PolygonsSegmentIndex> &segments) {
|
||||
assert(is_equal(*edge_a.vertex0(), *edge_b.vertex0()));
|
||||
CGAL::Orientation orientation;
|
||||
if (edge_a.is_linear() && edge_b.is_linear()) {
|
||||
@ -230,7 +230,7 @@ inline static CGAL::Orientation orientation_of_two_edges(const VD::edge_type &ed
|
||||
return orientation;
|
||||
}
|
||||
|
||||
static bool check_if_three_edges_are_ccw(const VD::edge_type &first, const VD::edge_type &second, const VD::edge_type &third, const std::vector<VoronoiUtils::Segment> &segments)
|
||||
static bool check_if_three_edges_are_ccw(const VD::edge_type &first, const VD::edge_type &second, const VD::edge_type &third, const std::vector<PolygonsSegmentIndex> &segments)
|
||||
{
|
||||
assert(is_equal(*first.vertex0(), *second.vertex0()) && is_equal(*second.vertex0(), *third.vertex0()));
|
||||
|
||||
@ -254,7 +254,7 @@ static bool check_if_three_edges_are_ccw(const VD::edge_type &first, const VD::e
|
||||
}
|
||||
}
|
||||
|
||||
bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VD &voronoi_diagram, const std::vector<VoronoiUtils::Segment> &segments)
|
||||
bool VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(const VD &voronoi_diagram, const std::vector<PolygonsSegmentIndex> &segments)
|
||||
{
|
||||
for (const VD::vertex_type &vertex : voronoi_diagram.vertices()) {
|
||||
std::vector<const VD::edge_type *> edges;
|
||||
|
@ -6,7 +6,7 @@
|
||||
#define slic3r_VoronoiUtilsCgal_hpp_
|
||||
|
||||
#include "Voronoi.hpp"
|
||||
#include "../Arachne/utils/VoronoiUtils.hpp"
|
||||
#include "../Arachne/utils/PolygonsSegmentIndex.hpp"
|
||||
|
||||
namespace Slic3r::Geometry {
|
||||
class VoronoiDiagram;
|
||||
@ -18,7 +18,7 @@ public:
|
||||
static bool is_voronoi_diagram_planar_intersection(const VoronoiDiagram &voronoi_diagram);
|
||||
|
||||
// Check if the Voronoi diagram is planar using verification that all neighboring edges are ordered CCW for each vertex.
|
||||
static bool is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram, const std::vector<Arachne::VoronoiUtils::Segment> &segments);
|
||||
static bool is_voronoi_diagram_planar_angle(const VoronoiDiagram &voronoi_diagram, const std::vector<Arachne::PolygonsSegmentIndex> &segments);
|
||||
|
||||
};
|
||||
} // namespace Slic3r::Geometry
|
||||
|
Loading…
x
Reference in New Issue
Block a user