Improve performance by migrating to Clipper2 2.0.1 (#15969)

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
This commit is contained in:
Ian Bassi
2026-10-02 17:33:41 -03:00
committed by GitHub
co-authored by Rodrigo Faselli
parent 70bc02467b
commit 222c6a2df5
78 changed files with 3028 additions and 7038 deletions
+2 -9
View File
@@ -14,7 +14,7 @@ static std::atomic<std::uint32_t> g_dbg_id = 0;
#endif
// Z for points from clip polygon
static constexpr auto CLIP_IDX = std::numeric_limits<ClipperLib_Z::cInt>::max();
static constexpr auto CLIP_IDX = std::numeric_limits<coord_t>::max();
static void cb_split_line(const ClipperZUtils::ZPoint& e1bot,
const ClipperZUtils::ZPoint& e1top,
@@ -91,14 +91,7 @@ SplittedLine do_split_line(const ClipperZUtils::ZPath& path, const ExPolygons& c
clip_path.emplace_back(ClipperZUtils::to_zpath<false>(hole.points, CLIP_IDX));
}
ClipperLib_Z::Clipper zclipper;
zclipper.PreserveCollinear(true);
zclipper.ZFillFunction(cb_split_line);
zclipper.AddPaths(clip_path, ClipperLib_Z::ptClip, true);
zclipper.AddPath(path, ClipperLib_Z::ptSubject, false);
ClipperLib_Z::PolyTree polytree;
zclipper.Execute(ClipperLib_Z::ctIntersection, polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(polytree), intersections);
intersections = ClipperZUtils::clip_zpaths(ctIntersection, ClipperZUtils::ZPaths{ path }, true, clip_path, cb_split_line, true);
}
if (intersections.empty()) {
return {};
+1 -1
View File
@@ -50,7 +50,7 @@ SplittedLine split_line(const PathType& path, const ExPolygons& clip, bool close
// Convert the input path into an open ZPath
ClipperZUtils::ZPath p;
p.reserve(path.size() + (closed ? 1 : 0));
ClipperLib_Z::cInt z = 0;
coord_t z = 0;
for (const auto& point : path) {
p.emplace_back(point.x(), point.y(), z);
z++;
+56 -93
View File
@@ -10,7 +10,7 @@
namespace Slic3r {
namespace Algorithm {
// Calculating radius discretization according to ClipperLib offsetter code, see void ClipperOffset::DoOffset(double delta)
// Calculating radius discretization according to the Clipper offsetter code, see ClipperOffset::DoGroupOffset()
inline double clipper_round_offset_error(double offset, double arc_tolerance)
{
static constexpr const double def_arc_tolerance = 0.25;
@@ -61,7 +61,6 @@ RegionExpansionParameters RegionExpansionParameters::build(
// Accuracy of the offsetter for wave propagation.
out.arc_tolerance = scaled<double>(0.1);
out.shortest_edge_length = out.initial_step * ClipperOffsetShortestEdgeFactor;
// Maximum inflation of seed contours over the boundary. Used to trim boundary to speed up
// clipping during wave propagation. Needs to be in sync with the offsetter accuracy.
@@ -75,25 +74,20 @@ RegionExpansionParameters RegionExpansionParameters::build(
// similar to expolygons_to_zpaths(), but each contour is expanded before converted to zpath.
// The expanded contours are then opened (the first point is repeated at the end).
static ClipperLib_Z::Paths expolygons_to_zpaths_expanded_opened(
static ClipperZUtils::ZPaths expolygons_to_zpaths_expanded_opened(
const ExPolygons &src, const float expansion, coord_t &base_idx)
{
ClipperLib_Z::Paths out;
ClipperZUtils::ZPaths out;
out.reserve(2 * std::accumulate(src.begin(), src.end(), size_t(0),
[](const size_t acc, const ExPolygon &expoly) { return acc + expoly.num_contours(); }));
ClipperLib::ClipperOffset offsetter;
offsetter.ShortestEdgeLength = expansion * ClipperOffsetShortestEdgeFactor;
ClipperLib::Paths expansion_cache;
for (const ExPolygon &expoly : src) {
for (size_t icontour = 0; icontour < expoly.num_contours(); ++ icontour) {
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output
// contours will be CCW oriented even though the input paths are CW oriented.
// Offset is applied after contour reorientation, thus the signum of the offset value is reversed.
offsetter.Clear();
offsetter.AddPath(expoly.contour_or_hole(icontour).points, ClipperLib::jtSquare, ClipperLib::etClosedPolygon);
expansion_cache.clear();
offsetter.Execute(expansion_cache, icontour == 0 ? expansion : -expansion);
append(out, ClipperZUtils::to_zpaths<true>(expansion_cache, base_idx));
// Orient CCW, then grow the contour and shrink the holes. The output contours are CCW.
Polygon contour = expoly.contour_or_hole(icontour);
if (! contour.is_counter_clockwise())
contour.reverse();
for (const Polygon &expanded : offset(contour, icontour == 0 ? expansion : -expansion, jtSquare))
out.emplace_back(ClipperZUtils::to_zpath<true>(expanded.points, base_idx));
}
++ base_idx;
}
@@ -104,21 +98,21 @@ static ClipperLib_Z::Paths expolygons_to_zpaths_expanded_opened(
// Thus some pieces of the clipped polygons may now become split at the ends of the source polygons.
// Those ends are sorted lexicographically in "splits".
// Reconnect those split pieces.
static inline void merge_splits(ClipperLib_Z::Paths &paths, std::vector<std::pair<ClipperLib_Z::IntPoint, int>> &splits)
static inline void merge_splits(ClipperZUtils::ZPaths &paths, std::vector<std::pair<ClipperZUtils::ZPoint, int>> &splits)
{
for (auto it_path = paths.begin(); it_path != paths.end(); ) {
ClipperLib_Z::Path &path = *it_path;
ClipperZUtils::ZPath &path = *it_path;
assert(path.size() >= 2);
bool merged = false;
if (path.size() >= 2) {
const ClipperLib_Z::IntPoint &front = path.front();
const ClipperLib_Z::IntPoint &back = path.back();
const ClipperZUtils::ZPoint &front = path.front();
const ClipperZUtils::ZPoint &back = path.back();
// The path before clipping was supposed to cross the clipping boundary or be fully out of it.
// Thus the clipped contour is supposed to become open, with one exception: The anchor expands into a closed hole.
if (front.x() != back.x() || front.y() != back.y()) {
// Look up the ends in "splits", possibly join the contours.
// "splits" maps into the other piece connected to the same end point.
auto find_end = [&splits](const ClipperLib_Z::IntPoint &pt) -> std::pair<ClipperLib_Z::IntPoint, int>* {
auto find_end = [&splits](const ClipperZUtils::ZPoint &pt) -> std::pair<ClipperZUtils::ZPoint, int>* {
auto it = std::lower_bound(splits.begin(), splits.end(), pt,
[](const auto &l, const auto &r){ return ClipperZUtils::zpoint_lower(l.first, r); });
return it != splits.end() && it->first == pt ? &(*it) : nullptr;
@@ -136,7 +130,7 @@ static inline void merge_splits(ClipperLib_Z::Paths &paths, std::vector<std::pai
end->second = int(it_path - paths.begin());
} else {
// Open end was found and matched with end->second
ClipperLib_Z::Path &other_path = paths[end->second];
ClipperZUtils::ZPath &other_path = paths[end->second];
polylines_merge(other_path, other_path.front() == end->first, std::move(path), end_front);
if (std::next(it_path) == paths.end()) {
paths.pop_back();
@@ -212,36 +206,29 @@ std::vector<WaveSeed> wave_seeds(
using Intersection = ClipperZUtils::ClipperZIntersectionVisitor::Intersection;
using Intersections = ClipperZUtils::ClipperZIntersectionVisitor::Intersections;
ClipperLib_Z::Paths segments;
Intersections intersections;
ClipperZUtils::ZPaths segments;
Intersections intersections;
coord_t idx_boundary_begin = 1;
coord_t idx_boundary_end = idx_boundary_begin;
coord_t idx_src_end;
coord_t idx_boundary_begin = 1;
coord_t idx_boundary_end = idx_boundary_begin;
coord_t idx_src_end;
{
ClipperLib_Z::Clipper zclipper;
ClipperZUtils::ClipperZIntersectionVisitor visitor(intersections);
zclipper.ZFillFunction(visitor.clipper_callback());
// as closed contours
zclipper.AddPaths(ClipperZUtils::expolygons_to_zpaths(boundary, idx_boundary_end), ClipperLib_Z::ptClip, true);
ClipperZUtils::ZPaths zboundary = ClipperZUtils::expolygons_to_zpaths(boundary, idx_boundary_end);
// as open contours
std::vector<std::pair<ClipperLib_Z::IntPoint, int>> zsrc_splits;
{
idx_src_end = idx_boundary_end;
ClipperLib_Z::Paths zsrc = expolygons_to_zpaths_expanded_opened(src, tiny_expansion, idx_src_end);
zclipper.AddPaths(zsrc, ClipperLib_Z::ptSubject, false);
zsrc_splits.reserve(zsrc.size());
for (const ClipperLib_Z::Path &path : zsrc) {
assert(path.size() >= 2);
assert(path.front() == path.back());
zsrc_splits.emplace_back(path.front(), -1);
}
std::sort(zsrc_splits.begin(), zsrc_splits.end(), [](const auto &l, const auto &r){ return ClipperZUtils::zpoint_lower(l.first, r.first); });
std::vector<std::pair<ClipperZUtils::ZPoint, int>> zsrc_splits;
idx_src_end = idx_boundary_end;
ClipperZUtils::ZPaths zsrc = expolygons_to_zpaths_expanded_opened(src, tiny_expansion, idx_src_end);
zsrc_splits.reserve(zsrc.size());
for (const ClipperZUtils::ZPath &path : zsrc) {
assert(path.size() >= 2);
assert(path.front() == path.back());
zsrc_splits.emplace_back(path.front(), -1);
}
ClipperLib_Z::PolyTree polytree;
zclipper.Execute(ClipperLib_Z::ctIntersection, polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(polytree), segments);
std::sort(zsrc_splits.begin(), zsrc_splits.end(), [](const auto &l, const auto &r){ return ClipperZUtils::zpoint_lower(l.first, r.first); });
segments = ClipperZUtils::clip_zpaths(ctIntersection, zsrc, true, zboundary, visitor.clipper_callback());
merge_splits(segments, zsrc_splits);
}
@@ -256,10 +243,10 @@ std::vector<WaveSeed> wave_seeds(
WaveSeeds out;
out.reserve(segments.size());
int iseed = 0;
for (const ClipperLib_Z::Path &path : segments) {
for (const ClipperZUtils::ZPath &path : segments) {
assert(path.size() >= 2);
ClipperLib_Z::IntPoint front = path.front();
ClipperLib_Z::IntPoint back = path.back();
ClipperZUtils::ZPoint front = path.front();
ClipperZUtils::ZPoint back = path.back();
// Both ends of a seed segment are supposed to be inside a single boundary expolygon.
// Thus as long as the seed contour is not closed, it should be open at a boundary point.
assert((front == back && front.z() >= idx_boundary_end && front.z() < idx_src_end) ||
@@ -280,7 +267,7 @@ std::vector<WaveSeed> wave_seeds(
// boundary ID is not directly available).
coord_t src_z = -1, boundary_z = -1;
// Scan all path points for the information we need.
for (const ClipperLib_Z::IntPoint &point : path) {
for (const ClipperZUtils::ZPoint &point : path) {
if (point.z() >= idx_boundary_end && point.z() < idx_src_end && src_z < 0)
src_z = point.z();
else if (point.z() >= idx_boundary_begin && point.z() < idx_boundary_end && boundary_z < 0)
@@ -311,7 +298,7 @@ std::vector<WaveSeed> wave_seeds(
// See https://github.com/prusa3d/PrusaSlicer/issues/12469.
// Segement is open, yet its first point seems to be part of boundary polygon.
// Take the first point with src polygon index.
for (const ClipperLib_Z::IntPoint &point : path) {
for (const ClipperZUtils::ZPoint &point : path) {
if (point.z() >= idx_boundary_end) {
front = point;
back = point;
@@ -360,17 +347,13 @@ std::vector<WaveSeed> wave_seeds(
return out;
}
static ClipperLib::Paths wavefront_initial(ClipperLib::ClipperOffset &co, const ClipperLib::Paths &polylines, float offset)
static Polygons wavefront_initial(const VecOfPoints &polylines, float offset, double arc_tolerance)
{
ClipperLib::Paths out;
Polygons out;
out.reserve(polylines.size());
ClipperLib::Paths out_this;
for (const ClipperLib::Path &path : polylines) {
for (const Points &path : polylines) {
assert(path.size() >= 2);
co.Clear();
co.AddPath(path, jtRound, path.front() == path.back() ? ClipperLib::etClosedLine : ClipperLib::etOpenRound);
co.Execute(out_this, offset);
append(out, std::move(out_this));
append(out, Slic3r::offset(Polyline(path), offset, jtRound, arc_tolerance, path.front() == path.back() ? etClosedLine : etOpenRound));
}
return out;
}
@@ -378,43 +361,24 @@ static ClipperLib::Paths wavefront_initial(ClipperLib::ClipperOffset &co, const
// Input polygons may consist of multiple expolygons, even nested expolygons.
// After inflation some polygons may thus overlap, however the overlap is being resolved during the successive
// clipping operation, thus it is not being done here.
static ClipperLib::Paths wavefront_step(ClipperLib::ClipperOffset &co, const ClipperLib::Paths &polygons, float offset)
static Polygons wavefront_step(const Polygons &polygons, float offset, double arc_tolerance)
{
ClipperLib::Paths out;
Polygons out;
out.reserve(polygons.size());
ClipperLib::Paths out_this;
for (const ClipperLib::Path &polygon : polygons) {
co.Clear();
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output
// contours will be CCW oriented even though the input paths are CW oriented.
// Offset is applied after contour reorientation, thus the signum of the offset value is reversed.
co.AddPath(polygon, jtRound, ClipperLib::etClosedPolygon);
bool ccw = ClipperLib::Orientation(polygon);
co.Execute(out_this, ccw ? offset : - offset);
if (! ccw) {
// Reverse the resulting contours.
for (ClipperLib::Path &path : out_this)
std::reverse(path.begin(), path.end());
}
append(out, std::move(out_this));
}
for (const Polygon &polygon : polygons)
// CCW contours grow, CW holes shrink.
append(out, Slic3r::offset(polygon, offset, jtRound, arc_tolerance));
return out;
}
static ClipperLib::Paths wavefront_clip(const ClipperLib::Paths &wavefront, const Polygons &clipping)
static Polygons wavefront_clip(const Polygons &wavefront, const Polygons &clipping)
{
ClipperLib::Clipper clipper;
clipper.AddPaths(wavefront, ClipperLib::ptSubject, true);
clipper.AddPaths(ClipperUtils::PolygonsProvider(clipping), ClipperLib::ptClip, true);
ClipperLib::Paths out;
clipper.Execute(ClipperLib::ctIntersection, out, ClipperLib::pftPositive, ClipperLib::pftPositive);
return out;
return intersection(wavefront, clipping, pftPositive);
}
static Polygons propagate_wave_from_boundary(
ClipperLib::ClipperOffset &co,
// Seed of the wave: Open polylines very close to the boundary.
const ClipperLib::Paths &seed,
const VecOfPoints &seed,
// Boundary inside which the waveform will propagate.
const ExPolygon &boundary,
// How much to inflate the seed lines to produce the first wave area.
@@ -425,25 +389,24 @@ static Polygons propagate_wave_from_boundary(
const size_t num_other_steps,
// Maximum inflation of seed contours over the boundary. Used to trim boundary to speed up
// clipping during wave propagation.
const float max_inflation)
const float max_inflation,
// Accuracy of the round offsets.
const double arc_tolerance)
{
assert(! seed.empty() && seed.front().size() >= 2);
Polygons clipping = ClipperUtils::clip_clipper_polygons_with_subject_bbox(boundary, get_extents<true>(seed).inflated(max_inflation));
ClipperLib::Paths polygons = wavefront_clip(wavefront_initial(co, seed, initial_step), clipping);
Polygons polygons = wavefront_clip(wavefront_initial(seed, initial_step, arc_tolerance), clipping);
// Now offset the remaining
for (size_t ioffset = 0; ioffset < num_other_steps; ++ ioffset)
polygons = wavefront_clip(wavefront_step(co, polygons, other_step), clipping);
return to_polygons(polygons);
polygons = wavefront_clip(wavefront_step(polygons, other_step, arc_tolerance), clipping);
return polygons;
}
// Resulting regions are sorted by boundary id and source id.
std::vector<RegionExpansion> propagate_waves(const WaveSeeds &seeds, const ExPolygons &boundary, const RegionExpansionParameters &params)
{
std::vector<RegionExpansion> out;
ClipperLib::Paths paths;
ClipperLib::ClipperOffset co;
co.ArcTolerance = params.arc_tolerance;
co.ShortestEdgeLength = params.shortest_edge_length;
VecOfPoints paths;
for (auto it_seed = seeds.begin(); it_seed != seeds.end();) {
auto it = it_seed;
paths.clear();
@@ -452,7 +415,7 @@ std::vector<RegionExpansion> propagate_waves(const WaveSeeds &seeds, const ExPol
// Propagate the wavefront while clipping it with the trimmed boundary.
// Collect the expanded polygons, merge them with the source polygons.
RegionExpansion re;
for (Polygon &polygon : propagate_wave_from_boundary(co, paths, boundary[it_seed->boundary], params.initial_step, params.other_step, params.num_other_steps, params.max_inflation))
for (Polygon &polygon : propagate_wave_from_boundary(paths, boundary[it_seed->boundary], params.initial_step, params.other_step, params.num_other_steps, params.max_inflation, params.arc_tolerance))
out.push_back({ std::move(polygon), it_seed->src, it_seed->boundary });
it_seed = it;
}
@@ -25,7 +25,6 @@ struct RegionExpansionParameters
// Accuracy of the offsetter for wave propagation.
double arc_tolerance;
double shortest_edge_length;
static RegionExpansionParameters build(
// Scaled expansion value
+4 -15
View File
@@ -263,10 +263,8 @@ std::unique_ptr<LocToLineGrid> cre
*/
void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
{
if (epsilon < 1) {
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
if (epsilon < 1)
return;
}
const int64_t half_epsilon = (epsilon + 1) / 2;
@@ -304,8 +302,6 @@ void fixSelfIntersections(const coord_t epsilon, Polygons &thiss)
}
}
}
ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(thiss), ClipperLib::pftEvenOdd);
}
/*!
@@ -352,18 +348,11 @@ void removeDegenerateVerts(Polygons &thiss)
void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool remove_holes)
{
auto to_path = [](const Polygon &poly) -> ClipperLib::Path {
ClipperLib::Path out;
for (const Point &pt : poly.points)
out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y()));
return out;
};
auto new_end = thiss.end();
if (remove_holes) {
for (auto it = thiss.begin(); it < new_end;) {
// All polygons smaller than target are removed by replacing them with a polygon from the back of the vector.
if (fabs(ClipperLib::Area(to_path(*it))) < min_area_size) {
if (fabs(it->area()) < min_area_size) {
--new_end;
*it = std::move(*new_end);
continue; // Don't increment the iterator such that the polygon just swapped in is checked next.
@@ -374,7 +363,7 @@ void removeSmallAreas(Polygons &thiss, const double min_area_size, const bool re
// For each polygon, computes the signed area, move small outlines at the end of the vector and keep pointer on small holes
Polygons small_holes;
for (auto it = thiss.begin(); it < new_end;) {
if (double area = ClipperLib::Area(to_path(*it)); fabs(area) < min_area_size) {
if (double area = it->area(); fabs(area) < min_area_size) {
if (area >= 0) {
--new_end;
if (it < new_end) {
@@ -773,7 +762,7 @@ void WallToolPaths::separateOutInnerContour()
//To get a correct shape, we need to make the outside contour positive and any holes inside negative.
//This can be done by applying the even-odd rule to the shape. This rule is not sensitive to the winding order of the polygon.
//The even-odd rule would be incorrect if the polygon self-intersects, but that should never be generated by the skeletal trapezoidation.
inner_contour = union_(inner_contour, ClipperLib::PolyFillType::pftEvenOdd);
inner_contour = union_(inner_contour, pftEvenOdd);
}
const Polygons& WallToolPaths::getInnerContour()
@@ -287,9 +287,9 @@ double ExtrusionLine::area() const
} // namespace Slic3r::Arachne
namespace Slic3r {
void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extrusion_paths, const ExtrusionRole role, const Flow &flow)
void extrusion_paths_append(ExtrusionPaths &dst, const ClipperZUtils::ZPaths &extrusion_paths, const ExtrusionRole role, const Flow &flow)
{
for (const ClipperLib_Z::Path &extrusion_path : extrusion_paths) {
for (const ClipperZUtils::ZPath &extrusion_path : extrusion_paths) {
ThickPolyline thick_polyline = Arachne::to_thick_polyline(extrusion_path);
Slic3r::append(dst, thick_polyline_to_multi_path(thick_polyline, role, flow, scaled<float>(0.05), float(SCALED_EPSILON)).paths);
}
@@ -5,7 +5,7 @@
#ifndef UTILS_EXTRUSION_LINE_H
#define UTILS_EXTRUSION_LINE_H
#include <clipper/clipper_z.hpp>
#include "../../ClipperZUtils.hpp"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
@@ -294,7 +294,7 @@ using VariableWidthLines = std::vector<ExtrusionLine>; //<! The ExtrusionLines g
namespace Slic3r {
void extrusion_paths_append(ExtrusionPaths &dst, const ClipperLib_Z::Paths &extrusion_paths, const ExtrusionRole role, const Flow &flow);
void extrusion_paths_append(ExtrusionPaths &dst, const ClipperZUtils::ZPaths &extrusion_paths, const ExtrusionRole role, const Flow &flow);
void extrusion_paths_append(ExtrusionPaths &dst, const Arachne::ExtrusionLine &extrusion, const ExtrusionRole role, const Flow &flow);
} // namespace Slic3r
+10 -7
View File
@@ -55,7 +55,7 @@ namespace Slic3r {
template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
const Slic3r::ClipperLib::IntPoint &v) noexcept
const Slic3r::Point &v) noexcept
{
return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.x()),
unscaled<Tout>(v.y())};
@@ -405,7 +405,7 @@ protected:
// 2) X distance of item corner to bed corner (low weight)
// 3) item row occupancy (useful when rotation is enabled)
// 4)需要允许往屏蔽区域的左边或下边去一点,不然很多物体可能认为摆不进去,实际上我们最后是可以做平移的
double dist_for_BOTTOM_LEFT(Box ibb, const ClipperLib::IntPoint& origin_pack)
double dist_for_BOTTOM_LEFT(Box ibb, const Slic3r::Point& origin_pack)
{
double dist_corner_y = ibb.minCorner().y() - origin_pack.y();
double dist_corner_x = ibb.minCorner().x() - origin_pack.x();
@@ -421,7 +421,7 @@ protected:
return bindist;
}
double dist_to_bin(const Box& ibb, const ClipperLib::IntPoint& origin_pack, typename Packer::PlacementConfig::Alignment starting_point_alignment)
double dist_to_bin(const Box& ibb, const Slic3r::Point& origin_pack, typename Packer::PlacementConfig::Alignment starting_point_alignment)
{
double bindist = 0;
if (starting_point_alignment == PConfig::Alignment::BOTTOM_LEFT)
@@ -439,7 +439,7 @@ protected:
// as it possibly can be but at the same time, it has to provide
// reasonable results.
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const Item &item, const ClipperLib::IntPoint &origin_pack)
objfunc(const Item &item, const Slic3r::Point &origin_pack)
{
const double bin_area = m_bin_area;
const SpatIndex& spatindex = m_rtree;
@@ -1014,7 +1014,12 @@ void _arrange(
inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};}
inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); }
inline ExPolygon to_nestbin(const Polygon &p) { return ExPolygon{p}; }
inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); }
// libnest2d's infinite box reaches the int64 limit, where Clipper2's double math is no longer exact.
inline Box to_nestbin(const InfiniteBed &bed)
{
const coord_t r = coord_t(1) << 50;
return Box{{bed.center.x() - r, bed.center.y() - r}, {bed.center.x() + r, bed.center.y() + r}};
}
inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); }
inline coord_t height(const BoundingBox& box) { return box.max.y() - box.min.y(); }
@@ -1122,8 +1127,6 @@ void arrange(ArrangePolygons & arrangables,
const BedT & bed,
const ArrangeParams & params)
{
namespace clppr = Slic3r::ClipperLib;
std::vector<Item> items, fixeditems;
items.reserve(arrangables.size());
+1 -5
View File
@@ -94,12 +94,9 @@ set(lisbslic3r_sources
calib.hpp
Circle.cpp
Circle.hpp
clipper.cpp
clipper.hpp
ClipperUtils.cpp
ClipperUtils.hpp
Clipper2Utils.cpp
Clipper2Utils.hpp
ClipperZUtils.cpp
ClipperZUtils.hpp
Color.cpp
Color.hpp
@@ -716,7 +713,6 @@ target_link_libraries(libslic3r
${OCCT_LIBS}
boost_libs
cereal::cereal
clipper
Clipper2
draco::draco
glu-libtess
-228
View File
@@ -1,228 +0,0 @@
#include "Clipper2Utils.hpp"
#include "libslic3r.h"
#include "clipper2/clipper.h"
namespace Slic3r {
//BBS: FIXME
Slic3r::Polylines Paths64_to_polylines(const Clipper2Lib::Paths64& in)
{
Slic3r::Polylines out;
out.reserve(in.size());
for (const Clipper2Lib::Path64& path64 : in) {
Slic3r::Points points;
points.reserve(path64.size());
for (const Clipper2Lib::Point64& point64 : path64)
points.emplace_back(Slic3r::Point(point64.x, point64.y));
out.emplace_back(Slic3r::Polyline(points));
}
return out;
}
//BBS: FIXME
template <typename Container>
Clipper2Lib::Paths64 Slic3rPoints_to_Paths64(const Container& in)
{
Clipper2Lib::Paths64 out;
out.reserve(in.size());
for (const auto& item : in) {
Clipper2Lib::Path64 path;
path.reserve(item.size());
for (const Slic3r::Point& point : item.points)
path.emplace_back(Clipper2Lib::Point64(point.x(), point.y()));
out.emplace_back(std::move(path));
}
return out;
}
Clipper2Lib::Paths64 Slic3rPolylines_to_Paths64(const Polylines& in)
{
return Slic3rPoints_to_Paths64(in);
}
Points Path64ToPoints(const Clipper2Lib::Path64& path64)
{
Points points;
points.reserve(path64.size());
for (const Clipper2Lib::Point64 &point64 : path64) points.emplace_back(Slic3r::Point(point64.x, point64.y));
return points;
}
static ExPolygons PolyTreeToExPolygons(Clipper2Lib::PolyTree64 &&polytree)
{
struct Inner
{
static void PolyTreeToExPolygonsRecursive(Clipper2Lib::PolyTree64 &&polynode, ExPolygons *expolygons)
{
size_t cnt = expolygons->size();
expolygons->resize(cnt + 1);
(*expolygons)[cnt].contour.points = Path64ToPoints(polynode.Polygon());
(*expolygons)[cnt].holes.resize(polynode.Count());
for (int i = 0; i < polynode.Count(); ++i) {
(*expolygons)[cnt].holes[i].points = Path64ToPoints(polynode[i]->Polygon());
// Add outer polygons contained by (nested within) holes.
for (int j = 0; j < polynode[i]->Count(); ++j) PolyTreeToExPolygonsRecursive(std::move(*polynode[i]->Child(j)), expolygons);
}
}
static size_t PolyTreeCountExPolygons(const Clipper2Lib::PolyPath64& polynode)
{
size_t cnt = 1;
for (size_t i = 0; i < polynode.Count(); ++i) {
for (size_t j = 0; j < polynode.Child(i)->Count(); ++j) cnt += PolyTreeCountExPolygons(*polynode.Child(i)->Child(j));
}
return cnt;
}
};
ExPolygons retval;
size_t cnt = 0;
for (int i = 0; i < polytree.Count(); ++i) cnt += Inner::PolyTreeCountExPolygons(*polytree[i]);
retval.reserve(cnt);
for (int i = 0; i < polytree.Count(); ++i) Inner::PolyTreeToExPolygonsRecursive(std::move(*polytree[i]), &retval);
return retval;
}
void SimplifyPolyTree(const Clipper2Lib::PolyPath64 &polytree, double epsilon, Clipper2Lib::PolyPath64 &result)
{
for (const auto &child : polytree) {
Clipper2Lib::PolyPath64 *newchild = result.AddChild(Clipper2Lib::SimplifyPath(child->Polygon(), epsilon));
SimplifyPolyTree(*child, epsilon, *newchild);
}
}
Clipper2Lib::Paths64 Slic3rPolygons_to_Paths64(const Polygons &in)
{
Clipper2Lib::Paths64 out;
out.reserve(in.size());
for (const Polygon &poly : in) {
Clipper2Lib::Path64 path;
path.reserve(poly.points.size());
for (const Slic3r::Point &point : poly.points) path.emplace_back(Clipper2Lib::Point64(point.x(), point.y()));
out.emplace_back(std::move(path));
}
return out;
}
Clipper2Lib::Paths64 Slic3rExPolygons_to_Paths64(const ExPolygons& in)
{
Clipper2Lib::Paths64 out;
out.reserve(in.size());
for (const ExPolygon& expolygon : in) {
for (size_t i = 0; i < expolygon.num_contours(); i++) {
const auto &poly = expolygon.contour_or_hole(i);
Clipper2Lib::Path64 path;
path.reserve(poly.points.size());
for (const Slic3r::Point &point : poly.points) path.emplace_back(Clipper2Lib::Point64(point.x(), point.y()));
out.emplace_back(std::move(path));
}
}
return out;
}
Polylines _clipper2_pl_open(Clipper2Lib::ClipType clipType, const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
{
Clipper2Lib::Clipper64 c;
c.AddOpenSubject(Slic3rPoints_to_Paths64(subject));
c.AddClip(Slic3rPoints_to_Paths64(clip));
Clipper2Lib::ClipType ct = clipType;
Clipper2Lib::FillRule fr = Clipper2Lib::FillRule::NonZero;
Clipper2Lib::Paths64 solution, solution_open;
c.Execute(ct, fr, solution, solution_open);
Slic3r::Polylines out;
out.reserve(solution.size() + solution_open.size());
polylines_append(out, Paths64_to_polylines(solution));
polylines_append(out, Paths64_to_polylines(solution_open));
return out;
}
Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
{ return _clipper2_pl_open(Clipper2Lib::ClipType::Intersection, subject, clip); }
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
{ return _clipper2_pl_open(Clipper2Lib::ClipType::Difference, subject, clip); }
ExPolygons union_ex_2(const Polygons& polygons)
{
Clipper2Lib::Clipper64 c;
c.AddSubject(Slic3rPolygons_to_Paths64(polygons));
Clipper2Lib::ClipType ct = Clipper2Lib::ClipType::Union;
Clipper2Lib::FillRule fr = Clipper2Lib::FillRule::NonZero;
Clipper2Lib::PolyTree64 solution;
c.Execute(ct, fr, solution);
ExPolygons results = PolyTreeToExPolygons(std::move(solution));
return results;
}
ExPolygons union_ex_2(const ExPolygons &expolygons)
{
Clipper2Lib::Clipper64 c;
c.AddSubject(Slic3rExPolygons_to_Paths64(expolygons));
Clipper2Lib::ClipType ct = Clipper2Lib::ClipType::Union;
Clipper2Lib::FillRule fr = Clipper2Lib::FillRule::NonZero;
Clipper2Lib::PolyTree64 solution;
c.Execute(ct, fr, solution);
ExPolygons results = PolyTreeToExPolygons(std::move(solution));
return results;
}
// 对 ExPolygons 进行偏移
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta)
{
Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons);
Clipper2Lib::ClipperOffset offsetter;
offsetter.AddPaths(subject, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon);
Clipper2Lib::PolyPath64 polytree;
offsetter.Execute(delta, polytree);
ExPolygons results = PolyTreeToExPolygons(std::move(polytree));
return results;
}
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta, Clipper2Lib::JoinType joinType)
{
Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons);
Clipper2Lib::ClipperOffset offsetter;
offsetter.AddPaths(subject, joinType, Clipper2Lib::EndType::Polygon);
Clipper2Lib::PolyPath64 polytree;
offsetter.Execute(delta, polytree);
ExPolygons results = PolyTreeToExPolygons(std::move(polytree));
return results;
}
ExPolygons offset2_ex_2(const ExPolygons& expolygons, double delta1, double delta2)
{
// 1st offset
Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons);
Clipper2Lib::ClipperOffset offsetter;
offsetter.AddPaths(subject, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon);
Clipper2Lib::PolyPath64 polytree;
offsetter.Execute(delta1, polytree);
// simplify the result
Clipper2Lib::PolyPath64 polytree2;
SimplifyPolyTree(polytree, SCALED_EPSILON, polytree2);
// 2nd offset
offsetter.Clear();
offsetter.AddPaths(Clipper2Lib::PolyTreeToPaths64(polytree2), Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Polygon);
polytree.Clear();
offsetter.Execute(delta2, polytree);
// convert back to expolygons
ExPolygons results = PolyTreeToExPolygons(std::move(polytree));
return results;
}
}
-22
View File
@@ -1,22 +0,0 @@
#ifndef slic3r_Clipper2Utils_hpp_
#define slic3r_Clipper2Utils_hpp_
#include "ExPolygon.hpp"
#include "Polygon.hpp"
#include "Polyline.hpp"
#include "clipper2/clipper.h"
namespace Slic3r {
Clipper2Lib::Paths64 Slic3rPolylines_to_Paths64(const Slic3r::Polylines& in);
Slic3r::Polylines Paths64_to_polylines(const Clipper2Lib::Paths64& in);
Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
ExPolygons union_ex_2(const Polygons &expolygons);
ExPolygons union_ex_2(const ExPolygons &expolygons);
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta);
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta, Clipper2Lib::JoinType joinType);
ExPolygons offset2_ex_2(const ExPolygons &expolygons, double delta1, double delta2);
}
#endif
-167
View File
@@ -1,167 +0,0 @@
#ifndef slic3r_Clipper2ZUtils_hpp_
#define slic3r_Clipper2ZUtils_hpp_
#include <numeric>
#include <vector>
#include <algorithm>
#include <clipper2/clipper2_z.hpp>
#include <libslic3r/Point.hpp>
namespace Slic3r { namespace Clipper2ZUtils {
using ZPoint64 = Clipper2Lib_Z::Point64;
using ZPoints64 = Clipper2Lib_Z::Path64;
using ZPath64 = Clipper2Lib_Z::Path64;
using ZPaths64 = Clipper2Lib_Z::Paths64;
inline bool zpoint64_lower(const ZPoint64 &l, const ZPoint64 &r) {
return l.x < r.x || (l.x == r.x && (l.y < r.y || (l.y == r.y && l.z < r.z)));
}
// Convert a single path to zpath with a given Z coordinate.
// If Open, then duplicate the first point at the end.
template<bool Open = false>
inline ZPath64 to_zpath64(const Points &path, int64_t z)
{
ZPath64 out;
if (!path.empty()) {
out.reserve(path.size() + (Open ? 1 : 0));
for (const Point &p : path) out.emplace_back(p.x(), p.y(), z);
if (Open) out.emplace_back(out.front());
}
return out;
}
template<bool Open = false>
inline ZPath64 to_zpath64(const Clipper2Lib_Z::Path64 &path, int64_t z)
{
ZPath64 out;
if (!path.empty()) {
out.reserve(path.size() + (Open ? 1 : 0));
for (const Clipper2Lib_Z::Point64 &p : path) out.emplace_back(p.x, p.y, z);
if (Open) out.emplace_back(out.front());
}
return out;
}
// Convert multiple paths to zpaths with a given Z coordinate.
template<bool Open = false>
inline ZPaths64 to_zpaths64(const VecOfPoints &paths, int64_t z)
{
ZPaths64 out;
out.reserve(paths.size());
for (const Points &path : paths) out.emplace_back(to_zpath64<Open>(path, z));
return out;
}
template<bool Open = false>
inline ZPaths64 to_zpaths64(const Clipper2Lib_Z::Paths64 &paths, int64_t z)
{
ZPaths64 out;
out.reserve(paths.size());
for (const Clipper2Lib_Z::Path64 &path : paths) out.emplace_back(to_zpath64<Open>(path, z));
return out;
}
// Convert multiple expolygons into zpaths with Z specified by index
// offset by base_idx.
template<bool Open = false>
inline ZPaths64 expolygons_to_zpaths64(const ExPolygons &src, int64_t &base_idx)
{
ZPaths64 out;
out.reserve(std::accumulate(src.begin(), src.end(), size_t(0),
[](const size_t acc, const ExPolygon &expoly) { return acc + expoly.num_contours(); }));
for (const ExPolygon &expoly : src) {
out.emplace_back(to_zpath64<Open>(expoly.contour.points, base_idx));
for (const Polygon &hole : expoly.holes)
out.emplace_back(to_zpath64<Open>(hole.points, base_idx));
++base_idx;
}
return out;
}
// Convert multiple expolygons into zpaths with the same Z.
template<bool Open = false>
inline ZPaths64 expolygons_to_zpaths64_with_same_z(const ExPolygons &src, int64_t z)
{
ZPaths64 out;
out.reserve(std::accumulate(src.begin(), src.end(), size_t(0),
[](const size_t acc, const ExPolygon &expoly) { return acc + expoly.num_contours(); }));
for (const ExPolygon &expoly : src) {
out.emplace_back(to_zpath64<Open>(expoly.contour.points, z));
for (const Polygon &hole : expoly.holes)
out.emplace_back(to_zpath64<Open>(hole.points, z));
}
return out;
}
// Convert a zpath back to 2D Points.
// If Open, then duplicate the first point at the end.
template<bool Open = false>
inline Points from_zpath64(const ZPath64 &path)
{
Points out;
if (!path.empty()) {
out.reserve(path.size() + (Open ? 1 : 0));
for (const ZPoint64 &p : path) out.emplace_back(p.x, p.y);
if (Open) out.emplace_back(out.front());
}
return out;
}
// Convert multiple zpaths back to 2D paths.
template<bool Open = false>
inline void from_zpaths64(const ZPaths64 &paths, VecOfPoints &out)
{
out.reserve(out.size() + paths.size());
for (const ZPath64 &path : paths) out.emplace_back(from_zpath64<Open>(path));
}
template<bool Open = false>
inline VecOfPoints from_zpaths64(const ZPaths64 &paths)
{
VecOfPoints out;
from_zpaths64<Open>(paths, out);
return out;
}
// Intersection visitor for Clipper2 (zCallback_).
class Clipper2ZIntersectionVisitor
{
public:
using Intersection = std::pair<int64_t, int64_t>;
using Intersections = std::vector<Intersection>;
Clipper2ZIntersectionVisitor(Intersections &intersections) : m_intersections(intersections) {}
void reset() { m_intersections.clear(); }
void operator()(const ZPoint64 &e1bot, const ZPoint64 &e1top, const ZPoint64 &e2bot, const ZPoint64 &e2top, ZPoint64 &pt)
{
std::array<int64_t, 4> srcs{e1bot.z, e1top.z, e2bot.z, e2top.z};
std::sort(srcs.begin(), srcs.end());
auto it = std::unique(srcs.begin(), srcs.end());
int new_size = std::distance(srcs.begin(), it);
assert(new_size == 1 || new_size == 2);
if (new_size == 1) {
pt.z = srcs[0];
}
else if(new_size == 2){
m_intersections.emplace_back(srcs[0], srcs[1]);
pt.z = -int64_t(m_intersections.size());
}
}
auto clipper_callback()
{
return [this](const ZPoint64 &e1bot, const ZPoint64 &e1top,
const ZPoint64 &e2bot, const ZPoint64 &e2top, ZPoint64 &pt) {
return (*this)(e1bot, e1top, e2bot, e2top, pt); };
}
const Intersections &intersections() const { return m_intersections; }
private:
Intersections &m_intersections;
};
}} // namespace Slic3r::Clipper2ZUtils
#endif // slic3r_Clipper2ZUtils_hpp_
File diff suppressed because it is too large Load Diff
+70 -156
View File
@@ -2,23 +2,32 @@
#define slic3r_ClipperUtils_hpp_
#include "libslic3r.h"
#include "clipper.hpp"
#include "ExPolygon.hpp"
#include "Polygon.hpp"
#include "Surface.hpp"
namespace Slic3r {
// Offset joins and ends, fill rules and boolean operations, named as in Clipper1.
enum JoinType { jtSquare, jtRound, jtMiter };
enum EndType { etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound };
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
} // namespace Slic3r
// import these wherever we're included
using Slic3r::ClipperLib::jtMiter;
using Slic3r::ClipperLib::jtRound;
using Slic3r::ClipperLib::jtSquare;
using Slic3r::jtMiter;
using Slic3r::jtRound;
using Slic3r::jtSquare;
namespace Slic3r {
static constexpr const float ClipperSafetyOffset = 10.f;
static constexpr const Slic3r::ClipperLib::JoinType DefaultJoinType = Slic3r::ClipperLib::jtMiter;
static constexpr const JoinType DefaultJoinType = jtMiter;
static constexpr const Slic3r::ClipperLib::EndType DefaultEndType = Slic3r::ClipperLib::etOpenButt;
static constexpr const EndType DefaultEndType = etOpenButt;
//FIXME evaluate the default miter limit. 3 seems to be extreme, Cura uses 1.2.
// Mitter Limit 3 is useful for perimeter generator, where sharp corners are extruded without needing a gap fill.
@@ -26,7 +35,7 @@ static constexpr const Slic3r::ClipperLib::EndType DefaultEndType = Sl
// is extended excessively.
static constexpr const double DefaultMiterLimit = 3.;
static constexpr const Slic3r::ClipperLib::JoinType DefaultLineJoinType = Slic3r::ClipperLib::jtSquare;
static constexpr const JoinType DefaultLineJoinType = jtSquare;
// Miter limit is ignored for jtSquare.
static constexpr const double DefaultLineMiterLimit = 0.;
@@ -304,12 +313,12 @@ namespace ClipperUtils {
};
// For ClipperLib with Z coordinates.
// Points with a Z coordinate.
using ZPoint = Vec3i32;
using ZPoints = std::vector<Vec3i32>;
// Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon.
// Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by one
// Useful as an optimization for expensive Clipper operations, for example when clipping source polygons one by one
// with a set of polygons covering the whole layer below.
void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out, const bool get_entire_polygons = false);
void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out);
@@ -323,31 +332,28 @@ namespace ClipperUtils {
}
// Perform union of input polygons using the non-zero rule, convert to ExPolygons.
ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool do_union = false);
// offset Polygons
// Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better.
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
// offset Polylines
// Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better.
// Input polygons for negative offset shall be "normalized": There must be no overlap / intersections between the input polygons.
Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polyline3 &polyline, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::SurfacesPtr &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::SurfacesPtr &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polyline3 &polyline, const float delta, JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::Surfaces &surfaces, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::SurfacesPtr &surfaces, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::Surfaces &surfaces, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::SurfacesPtr &surfaces, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
// BBS
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{
Slic3r::Polygons temp;
temp.push_back(polygon);
@@ -356,8 +362,8 @@ inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float
}
// convert stroke to path by offsetting of contour
Polygons contour_to_polygons(const Polygon &polygon, const float line_width, ClipperLib::JoinType join_type = DefaultJoinType, double miter_limit = DefaultMiterLimit);
Polygons contour_to_polygons(const Polygons &polygon, const float line_width, ClipperLib::JoinType join_type = DefaultJoinType, double miter_limit = DefaultMiterLimit);
Polygons contour_to_polygons(const Polygon &polygon, const float line_width, JoinType join_type = DefaultJoinType, double miter_limit = DefaultMiterLimit);
Polygons contour_to_polygons(const Polygons &polygon, const float line_width, JoinType join_type = DefaultJoinType, double miter_limit = DefaultMiterLimit);
inline Slic3r::Polygons union_safety_offset (const Slic3r::Polygons &polygons) { return offset (polygons, ClipperSafetyOffset); }
inline Slic3r::Polygons union_safety_offset (const Slic3r::ExPolygons &expolygons) { return offset (expolygons, ClipperSafetyOffset); }
@@ -370,62 +376,62 @@ Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::Polygons &polygons);
Slic3r::ExPolygons union_safety_offset_ex(const Slic3r::ExPolygons &expolygons);
// Aliases for the various offset(...) functions, conveying the purpose of the offset.
inline Slic3r::Polygons expand(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::Polygons expand(const Slic3r::Polygon &polygon, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset(polygon, delta, joinType, miterLimit); }
inline Slic3r::Polygons expand(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::Polygons expand(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset(polygons, delta, joinType, miterLimit); }
inline Slic3r::Polygons expand(const Slic3r::ExPolygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::Polygons expand(const Slic3r::ExPolygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset(polygons, delta, joinType, miterLimit); }
inline Slic3r::ExPolygons expand_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::ExPolygons expand_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset_ex(polygons, delta, joinType, miterLimit); }
// Input polygons for shrinking shall be "normalized": There must be no overlap / intersections between the input polygons.
inline Slic3r::Polygons shrink(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::Polygons shrink(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset(polygons, -delta, joinType, miterLimit); }
inline Slic3r::ExPolygons shrink_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::ExPolygons shrink_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset_ex(polygons, -delta, joinType, miterLimit); }
inline Slic3r::ExPolygons shrink_ex(const Slic3r::ExPolygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::ExPolygons shrink_ex(const Slic3r::ExPolygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset_ex(polygons, -delta, joinType, miterLimit); }
// Wherever applicable, please use the opening() / closing() variants instead, they convey their purpose better.
// Input polygons for negative offset shall be "normalized": There must be no overlap / intersections between the input polygons.
Slic3r::Polygons offset2(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset2_ex(const Slic3r::Surfaces &surfaces, const float delta1, const float delta2, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset2(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::ExPolygons offset2_ex(const Slic3r::Surfaces &surfaces, const float delta1, const float delta2, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
// BBS
Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType,
Slic3r::ExPolygons _clipper_ex(ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
// Offset outside, then inside produces morphological closing. All deltas should be positive.
Slic3r::Polygons closing(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
inline Slic3r::Polygons closing(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
Slic3r::Polygons closing(const Slic3r::Polygons &polygons, const float delta1, const float delta2, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
inline Slic3r::Polygons closing(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ return closing(polygons, delta, delta, joinType, miterLimit); }
Slic3r::ExPolygons closing_ex(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
inline Slic3r::ExPolygons closing_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
Slic3r::ExPolygons closing_ex(const Slic3r::Polygons &polygons, const float delta1, const float delta2, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
inline Slic3r::ExPolygons closing_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ return closing_ex(polygons, delta, delta, joinType, miterLimit); }
inline Slic3r::ExPolygons closing_ex(const Slic3r::ExPolygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::ExPolygons closing_ex(const Slic3r::ExPolygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset2_ex(polygons, delta, - delta, joinType, miterLimit); }
inline Slic3r::ExPolygons closing_ex(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::ExPolygons closing_ex(const Slic3r::Surfaces &surfaces, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset2_ex(surfaces, delta, - delta, joinType, miterLimit); }
// Offset inside, then outside produces morphological opening. All deltas should be positive.
// Input polygons for opening shall be "normalized": There must be no overlap / intersections between the input polygons.
Slic3r::Polygons opening(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons opening(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons opening(const Slic3r::Surfaces &surfaces, const float delta1, const float delta2, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
inline Slic3r::Polygons opening(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
Slic3r::Polygons opening(const Slic3r::Polygons &polygons, const float delta1, const float delta2, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons opening(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons opening(const Slic3r::Surfaces &surfaces, const float delta1, const float delta2, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
inline Slic3r::Polygons opening(const Slic3r::Polygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ return opening(polygons, delta, delta, joinType, miterLimit); }
inline Slic3r::Polygons opening(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::Polygons opening(const Slic3r::ExPolygons &expolygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ return opening(expolygons, delta, delta, joinType, miterLimit); }
inline Slic3r::Polygons opening(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::Polygons opening(const Slic3r::Surfaces &surfaces, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ return opening(surfaces, delta, delta, joinType, miterLimit); }
inline Slic3r::ExPolygons opening_ex(const Slic3r::ExPolygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::ExPolygons opening_ex(const Slic3r::ExPolygons &polygons, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset2_ex(polygons, - delta, delta, joinType, miterLimit); }
inline Slic3r::ExPolygons opening_ex(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
inline Slic3r::ExPolygons opening_ex(const Slic3r::Surfaces &surfaces, const float delta, JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit)
{ assert(delta > 0); return offset2_ex(surfaces, - delta, delta, joinType, miterLimit); }
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, const Slic3r::Polygons &clip);
Slic3r::Lines _clipper_ln(ClipType clipType, const Slic3r::Lines &subject, const Slic3r::Polygons &clip);
// Safety offset is applied to the clipping polygons only.
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
@@ -489,13 +495,14 @@ inline Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons& subject, const Slic3
inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
{
return _clipper_ln(ClipperLib::ctDifference, subject, clip);
return _clipper_ln(ctDifference, subject, clip);
}
// Safety offset is applied to the clipping polygons only.
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, PolyFillType fill_type);
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox().
// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon.
@@ -534,135 +541,42 @@ void restore_source_path_order(const Slic3r::Polyline &source, Slic3r::Polylines
inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
{
return _clipper_ln(ClipperLib::ctIntersection, subject, clip);
return _clipper_ln(ctIntersection, subject, clip);
}
inline Slic3r::Lines intersection_ln(const Slic3r::Line &subject, const Slic3r::Polygons &clip)
{
Slic3r::Lines lines;
lines.emplace_back(subject);
return _clipper_ln(ClipperLib::ctIntersection, lines, clip);
return _clipper_ln(ctIntersection, lines, clip);
}
Slic3r::Polygons union_(const Slic3r::Polygons &subject);
Slic3r::Polygons union_(const Slic3r::ExPolygons &subject);
Slic3r::Polygons union_(const Slic3r::Polygons &subject, const ClipperLib::PolyFillType fillType);
Slic3r::Polygons union_(const Slic3r::Polygons &subject, const PolyFillType fillType);
Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2);
// May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative).
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, ClipperLib::PolyFillType fill_type = ClipperLib::pftNonZero);
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, PolyFillType fill_type = pftNonZero);
Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject);
Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &subject2);
Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject);
Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons& poly1, const Slic3r::ExPolygons& poly2, bool safety_offset_ = false);
// Convert polygons / expolygons into ClipperLib::PolyTree using ClipperLib::pftEvenOdd, thus union will NOT be performed.
// If the contours are not intersecting, their orientation shall not be modified by union_pt().
ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject);
ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject);
Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygon &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons xor_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject);
ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes);
// Implementing generalized loop (foreach) over a list of nodes which can be
// ordered or unordered (performance gain) based on template parameter
enum class e_ordering {
ON,
OFF
};
// Create a template struct, template functions can not be partially specialized
template<e_ordering o, class Fn> struct _foreach_node {
void operator()(const ClipperLib::PolyNodes &nodes, Fn &&fn);
};
// Specialization with NO ordering
template<class Fn> struct _foreach_node<e_ordering::OFF, Fn> {
void operator()(const ClipperLib::PolyNodes &nodes, Fn &&fn)
{
for (auto &n : nodes) fn(n);
}
};
// Specialization with ordering
template<class Fn> struct _foreach_node<e_ordering::ON, Fn> {
void operator()(const ClipperLib::PolyNodes &nodes, Fn &&fn)
{
auto ordered_nodes = order_nodes(nodes);
for (auto &n : nodes) fn(n);
}
};
// Wrapper function for the foreach_node which can deduce arguments automatically
template<e_ordering o, class Fn>
void foreach_node(const ClipperLib::PolyNodes &nodes, Fn &&fn)
{
_foreach_node<o, Fn>()(nodes, std::forward<Fn>(fn));
}
// Collecting polygons of the tree into a list of Polygons, holes have clockwise
// orientation.
template<e_ordering ordering = e_ordering::OFF>
void traverse_pt(const ClipperLib::PolyNode *tree, Polygons *out)
{
if (!tree) return; // terminates recursion
// Push the contour of the current level
out->emplace_back(tree->Contour);
// Do the recursion for all the children.
traverse_pt<ordering>(tree->Childs, out);
}
// Collecting polygons of the tree into a list of ExPolygons.
template<e_ordering ordering = e_ordering::OFF>
void traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *out)
{
if (!tree) return;
else if(tree->IsHole()) {
// Levels of holes are skipped and handled together with the
// contour levels.
traverse_pt<ordering>(tree->Childs, out);
return;
}
ExPolygon level;
level.contour.points = tree->Contour;
foreach_node<ordering>(tree->Childs,
[out, &level] (const ClipperLib::PolyNode *node) {
// Holes are collected here.
level.holes.emplace_back(node->Contour);
// By doing a recursion, a new level expoly is created with the contour
// and holes of the lower level. Doing this for all the childs.
traverse_pt<ordering>(node->Childs, out);
});
out->emplace_back(level);
}
template<e_ordering o = e_ordering::OFF, class ExOrJustPolygons>
void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval)
{
foreach_node<o>(nodes, [&retval](const ClipperLib::PolyNode *node) {
traverse_pt<o>(node, retval);
});
}
/* OTHER */
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject);
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject);
Polygons top_level_islands(const Slic3r::Polygons &polygons);
// Top level expolygons of the even-odd union, the ones nested in their holes go to `nested`.
ExPolygons top_level_expolygons(const ExPolygons &expolygons, ExPolygons *nested = nullptr);
ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit);
Points mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit);
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
+77
View File
@@ -0,0 +1,77 @@
#include "ClipperZUtils.hpp"
#include <clipper2/clipper2_z.hpp>
namespace Slic3r {
namespace ClipperZUtils {
namespace C2 = Clipper2Lib_Z;
static C2::Paths64 to_paths64(const ZPaths &paths)
{
C2::Paths64 out;
out.reserve(paths.size());
for (const ZPath &path : paths) {
C2::Path64 &dst = out.emplace_back();
dst.reserve(path.size());
for (const ZPoint &pt : path)
dst.emplace_back(pt.x(), pt.y(), pt.z());
}
return out;
}
static void append_zpaths(const C2::Paths64 &paths, ZPaths &out)
{
out.reserve(out.size() + paths.size());
for (const C2::Path64 &path : paths) {
ZPath &dst = out.emplace_back();
dst.reserve(path.size());
for (const C2::Point64 &pt : path)
dst.emplace_back(pt.x, pt.y, pt.z);
}
}
double area(const ZPath &path)
{
const size_t size = path.size();
if (size < 3)
return 0.;
double a = 0.;
for (size_t i = 0, j = size - 1; i < size; j = i ++)
a += (double(path[j].x()) + path[i].x()) * (double(path[j].y()) - path[i].y());
return -a * 0.5;
}
ZPaths clip_zpaths(ClipType clip_type, const ZPaths &subject, bool subject_open, const ZPaths &clip, const ZFillCallback &zfill, bool preserve_collinear)
{
C2::Clipper64 clipper;
clipper.PreserveCollinear(preserve_collinear);
if (zfill)
clipper.SetZCallback([&zfill](const C2::Point64 &e1bot, const C2::Point64 &e1top, const C2::Point64 &e2bot, const C2::Point64 &e2top, C2::Point64 &pt) {
// Clipper2 already copied Z from a coincident input vertex.
auto coincident = [&pt](const C2::Point64 &p) { return p.x == pt.x && p.y == pt.y; };
if (coincident(e1bot) || coincident(e1top) || coincident(e2bot) || coincident(e2top))
return;
ZPoint zpt(pt.x, pt.y, pt.z);
zfill(ZPoint(e1bot.x, e1bot.y, e1bot.z), ZPoint(e1top.x, e1top.y, e1top.z),
ZPoint(e2bot.x, e2bot.y, e2bot.z), ZPoint(e2top.x, e2top.y, e2top.z), zpt);
pt.z = zpt.z();
});
if (subject_open)
clipper.AddOpenSubject(to_paths64(subject));
else
clipper.AddSubject(to_paths64(subject));
clipper.AddClip(to_paths64(clip));
const C2::ClipType type = clip_type == ctIntersection ? C2::ClipType::Intersection :
clip_type == ctUnion ? C2::ClipType::Union :
clip_type == ctDifference ? C2::ClipType::Difference : C2::ClipType::Xor;
C2::Paths64 closed, open;
clipper.Execute(type, C2::FillRule::NonZero, closed, open);
ZPaths out;
append_zpaths(closed, out);
append_zpaths(open, out);
return out;
}
} // namespace ClipperZUtils
} // namespace Slic3r
+17 -7
View File
@@ -1,10 +1,11 @@
#ifndef slic3r_ClipperZUtils_hpp_
#define slic3r_ClipperZUtils_hpp_
#include <functional>
#include <numeric>
#include <vector>
#include <clipper/clipper_z.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/Point.hpp>
#include <libslic3r/ExPolygon.hpp>
@@ -12,10 +13,13 @@ namespace Slic3r {
namespace ClipperZUtils {
using ZPoint = ClipperLib_Z::IntPoint;
using ZPoints = ClipperLib_Z::Path;
using ZPath = ClipperLib_Z::Path;
using ZPaths = ClipperLib_Z::Paths;
using ZPoint = Vec3crd;
using ZPoints = std::vector<ZPoint, PointsAllocator<ZPoint>>;
using ZPath = ZPoints;
using ZPaths = std::vector<ZPath, PointsAllocator<ZPath>>;
// Sets Z of an intersection point from the edges crossing there.
using ZFillCallback = std::function<void(const ZPoint &e1bot, const ZPoint &e1top, const ZPoint &e2bot, const ZPoint &e2top, ZPoint &pt)>;
inline bool zpoint_lower(const ZPoint &l, const ZPoint &r)
{
@@ -101,6 +105,12 @@ inline VecOfPoints from_zpaths(const ZPaths &paths)
return out;
}
// Signed area, positive for a CCW path.
double area(const ZPath &path);
// Non-zero rule boolean, the subject may be open. zfill only sees intersections off the input vertices.
ZPaths clip_zpaths(ClipType clip_type, const ZPaths &subject, bool subject_open, const ZPaths &clip, const ZFillCallback &zfill, bool preserve_collinear = false);
class ClipperZIntersectionVisitor {
public:
using Intersection = std::pair<coord_t, coord_t>;
@@ -126,8 +136,8 @@ public:
}
}
}
ClipperLib_Z::ZFillCallback clipper_callback() {
return [this](const ZPoint &e1bot, const ZPoint &e1top,
ZFillCallback clipper_callback() {
return [this](const ZPoint &e1bot, const ZPoint &e1top,
const ZPoint &e2bot, const ZPoint &e2top, ZPoint &pt)
{ return (*this)(e1bot, e1top, e2bot, e2top, pt); };
}
+1 -1
View File
@@ -3519,7 +3519,7 @@ ExPolygon priv::to_expoly(const SurfacePatch &patch, const Project &projection,
{
Polygons polys = unproject_loops(patch, projection, depth_range);
// should not be used when no opposit triangle are counted so should not create overlaps
ClipperLib::PolyFillType fill_type = ClipperLib::PolyFillType::pftEvenOdd;
PolyFillType fill_type = pftEvenOdd;
ExPolygons expolys = Slic3r::union_ex(polys, fill_type);
if (expolys.size() == 1)
return expolys.front();
@@ -1,5 +1,3 @@
#include "clipper/clipper_z.hpp"
#include "libslic3r.h"
#include "ClipperUtils.hpp"
#include "EdgeGrid.hpp"
+77 -6
View File
@@ -404,18 +404,89 @@ bool Emboss::divide_segments_for_close_point(ExPolygons &expolygons, double dist
return true;
}
// Clipper1's CleanPolygon(): removes vertices closer than `distance` to a neighbour or to the line through their neighbours.
static void clean_polygon(Points &points, double distance)
{
auto points_are_close = [](const Point &a, const Point &b, double dist2) {
const double dx = double(a.x() - b.x()), dy = double(a.y() - b.y());
return dx * dx + dy * dy <= dist2;
};
auto distance_from_line2 = [](const Point &pt, const Point &ln1, const Point &ln2) {
const double A = double(ln1.y() - ln2.y());
const double B = double(ln2.x() - ln1.x());
const double C = A * pt.x() + B * pt.y() - (A * ln1.x() + B * ln1.y());
return (C * C) / (A * A + B * B);
};
auto slopes_near_collinear = [&distance_from_line2](const Point &pt1, const Point &pt2, const Point &pt3, double dist2) {
if (std::abs(pt1.x() - pt2.x()) > std::abs(pt1.y() - pt2.y())) {
if ((pt1.x() > pt2.x()) == (pt1.x() < pt3.x()))
return distance_from_line2(pt1, pt2, pt3) < dist2;
if ((pt2.x() > pt1.x()) == (pt2.x() < pt3.x()))
return distance_from_line2(pt2, pt1, pt3) < dist2;
return distance_from_line2(pt3, pt1, pt2) < dist2;
}
if ((pt1.y() > pt2.y()) == (pt1.y() < pt3.y()))
return distance_from_line2(pt1, pt2, pt3) < dist2;
if ((pt2.y() > pt1.y()) == (pt2.y() < pt3.y()))
return distance_from_line2(pt2, pt1, pt3) < dist2;
return distance_from_line2(pt3, pt1, pt2) < dist2;
};
size_t size = points.size();
if (size == 0)
return;
std::vector<size_t> next(size), prev(size);
std::vector<char> done(size, 0);
for (size_t i = 0; i < size; ++i) {
next[i] = (i + 1) % size;
prev[next[i]] = i;
}
auto exclude = [&next, &prev, &done](size_t op) {
const size_t result = prev[op];
next[result] = next[op];
prev[next[op]] = result;
done[result] = 0;
return result;
};
const double dist2 = distance * distance;
size_t op = 0;
while (! done[op] && next[op] != prev[op]) {
if (points_are_close(points[op], points[prev[op]], dist2)) {
op = exclude(op);
-- size;
} else if (points_are_close(points[prev[op]], points[next[op]], dist2)) {
exclude(next[op]);
op = exclude(op);
size -= 2;
} else if (slopes_near_collinear(points[prev[op]], points[op], points[next[op]], dist2)) {
op = exclude(op);
-- size;
} else {
done[op] = 1;
op = next[op];
}
}
if (size < 3)
size = 0;
Points out;
out.reserve(size);
for (size_t i = 0; i < size; ++i) {
out.emplace_back(points[op]);
op = next[op];
}
points = std::move(out);
}
HealedExPolygons Emboss::heal_polygons(const Polygons &shape, bool is_non_zero, unsigned int max_iteration)
{
const double clean_distance = 1.415; // little grater than sqrt(2)
ClipperLib::PolyFillType fill_type = is_non_zero ?
ClipperLib::pftNonZero : ClipperLib::pftEvenOdd;
PolyFillType fill_type = is_non_zero ? pftNonZero : pftEvenOdd;
// When edit this code check that font 'ALIENATE.TTF' and glyph 'i' still work
// fix of self intersections
// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Functions/SimplifyPolygon.htm
ClipperLib::Paths paths = ClipperLib::SimplifyPolygons(ClipperUtils::PolygonsProvider(shape), fill_type);
ClipperLib::CleanPolygons(paths, clean_distance);
Polygons polygons = to_polygons(paths);
Polygons polygons = union_(shape, fill_type);
for (Polygon &polygon : polygons)
clean_polygon(polygon.points, clean_distance);
polygons.erase(std::remove_if(polygons.begin(), polygons.end(),
[](const Polygon &p) { return p.size() < 3; }), polygons.end());
+1 -1
View File
@@ -165,7 +165,7 @@ namespace Emboss
/// Fix duplicit points and self intersections in polygons.
/// Also try to reduce amount of points and remove useless polygon parts
/// </summary>
/// <param name="is_non_zero">Fill type ClipperLib::pftNonZero for overlapping otherwise </param>
/// <param name="is_non_zero">Fill type pftNonZero for overlapping otherwise </param>
/// <param name="max_iteration">Look at heal_expolygon()::max_iteration</param>
/// <returns>Healed shapes with flag is fully healed</returns>
HealedExPolygons heal_polygons(const Polygons &shape, bool is_non_zero = true, unsigned max_iteration = 10);
+6 -25
View File
@@ -3,7 +3,6 @@
#include <cmath>
#include "../ClipperUtils.hpp"
#include "../Clipper2Utils.hpp"
#include "../EdgeGrid.hpp"
#include "../Geometry.hpp"
#include "../Geometry/Circle.hpp"
@@ -2734,11 +2733,8 @@ void multiline_fill(Polylines& polylines, const FillParams& params, float spacin
if (polylines.empty())
return;
// Convert source polylines to Clipper2 paths
Clipper2Lib::Paths64 subject_paths = Slic3rPolylines_to_Paths64(polylines);
const double miter_limit = 2.0;
const int rings = n_lines / 2;
const int rings = n_lines / 2;
// Compute offsets (in units of spacing)
std::vector<double> offsets;
@@ -2757,10 +2753,6 @@ void multiline_fill(Polylines& polylines, const FillParams& params, float spacin
offsets.push_back(start + i * spacing);
}
// Process each offset
Clipper2Lib::ClipperOffset offsetter(miter_limit);
offsetter.AddPaths(subject_paths, Clipper2Lib::JoinType::Round, Clipper2Lib::EndType::Round);
for (double t : offsets) {
if (t == 0.0) {
// Center line (only applies when n_lines is odd)
@@ -2768,22 +2760,11 @@ void multiline_fill(Polylines& polylines, const FillParams& params, float spacin
continue;
}
// ClipperOffset with current offset distance (union is not needed here)
Clipper2Lib::Paths64 offset_paths;
offsetter.Execute(scale_(t), offset_paths);
if (offset_paths.empty())
continue;
// Convert back to polylines
Polylines new_polylines = Paths64_to_polylines(offset_paths);
for (Polyline& pl : new_polylines) {
if (pl.points.size() < 3)
continue;
if (pl.points.front() != pl.points.back())
pl.points.push_back(pl.points.front());
all_polylines.emplace_back(std::move(pl));
}
const float delta = float(scale_(t));
// Arc tolerance of 1/500 of the offset, Clipper2's default.
for (const Polygon &ring : offset(polylines, delta, jtRound, 0.002 * delta, etOpenRound))
if (ring.size() >= 3)
all_polylines.emplace_back(ring.split_at_first_point());
}
polylines = std::move(all_polylines);
+2 -2
View File
@@ -417,9 +417,9 @@ public:
// bool sticks_removed =
remove_sticks(polygons_src);
// if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!";
polygons_outer = aoffset1 == 0 ? to_polygons(polygons_src) : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit);
polygons_outer = aoffset1 == 0 ? to_polygons(polygons_src) : offset(polygons_src, float(aoffset1), jtMiter, miterLimit);
if (aoffset2 < 0)
polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit);
polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), jtMiter, miterLimit);
// Filter out contours with zero area or small area, contours with 2 points only.
const double min_area_threshold = 0.01 * aoffset2 * aoffset2;
remove_small(polygons_outer, min_area_threshold);
+1 -8
View File
@@ -143,14 +143,7 @@ NodeSPtr Node::closestNode(const Point& loc)
bool inside(const Polygons &polygons, const Point &p)
{
int poly_count_inside = 0;
for (const Polygon &poly : polygons) {
const int is_inside_this_poly = ClipperLib::PointInPolygon(p, poly.points);
if (is_inside_this_poly == -1)
return true;
poly_count_inside += is_inside_this_poly;
}
return (poly_count_inside % 2) == 1;
return contains(polygons, p, true);
}
bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist)
+7 -19
View File
@@ -20,7 +20,6 @@
#include "TopExp_Explorer.hxx"
#include "TopoDS.hxx"
#include "BRepExtrema_SelfIntersection.hxx"
#include "libslic3r/clipper.hpp"
#include "libslic3r/Polygon.hpp"
namespace Slic3r {
@@ -211,26 +210,15 @@ bool get_svg_profile(const char *path, std::vector<Element_Info> &element_infos,
Polygons polygons;
bool close_polygon = false;
for (int i = 0; i < path_line_points.size(); ++i) {
ClipperLib::Path pt_path;
for (auto line_point : path_line_points[i]) {
pt_path.push_back(ClipperLib::IntPoint(line_point.first.X() * scale_size, line_point.first.Y() * scale_size));
Polyline pt_path;
for (auto line_point : path_line_points[i]) {
pt_path.points.push_back(Point(line_point.first.X() * scale_size, line_point.first.Y() * scale_size));
}
pt_path.push_back(ClipperLib::IntPoint(path_line_points[i].back().second.X() * scale_size, path_line_points[i].back().second.Y() * scale_size));
pt_path.points.push_back(Point(path_line_points[i].back().second.X() * scale_size, path_line_points[i].back().second.Y() * scale_size));
ClipperLib::Paths out_paths;
ClipperLib::ClipperOffset co;
if (pt_path.front() == pt_path.back()) {
co.AddPath(pt_path, ClipperLib::jtMiter, ClipperLib::etClosedLine);
close_polygon = true;
} else {
co.AddPath(pt_path, ClipperLib::jtMiter, ClipperLib::etOpenSquare);
close_polygon = false;
}
co.Execute(out_paths, stroke_width / 2);
for (auto out_path : out_paths) {
polygons.emplace_back(Polygon(out_path));
}
close_polygon = pt_path.points.front() == pt_path.points.back();
if (stroke_width > 0)
append(polygons, offset(pt_path, stroke_width / 2, jtMiter, 2., close_polygon ? etClosedLine : etOpenSquare));
}
if (!close_polygon)
-1
View File
@@ -4,7 +4,6 @@
#include "ClipperUtils.hpp"
#include "ExPolygon.hpp"
#include "Line.hpp"
#include "clipper.hpp"
#include <algorithm>
#include <cassert>
#include <cmath>
+3 -3
View File
@@ -627,9 +627,9 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
}
#else
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS jtMiter, 3.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS jtMiter, 1.5
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS jtSquare, 0.
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
{
+9 -9
View File
@@ -500,26 +500,26 @@ Polylines to_dashes(const Polyline &polyline, const DashesParam& param)
HealedExPolygons stroke_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams &param)
{
// convert stroke to polygon
ClipperLib::JoinType join_type = ClipperLib::JoinType::jtSquare;
JoinType join_type = jtSquare;
switch (static_cast<NSVGlineJoin>(shape.strokeLineJoin)) {
case NSVGlineJoin::NSVG_JOIN_BEVEL: join_type = ClipperLib::JoinType::jtSquare; break;
case NSVGlineJoin::NSVG_JOIN_MITER: join_type = ClipperLib::JoinType::jtMiter; break;
case NSVGlineJoin::NSVG_JOIN_ROUND: join_type = ClipperLib::JoinType::jtRound; break;
case NSVGlineJoin::NSVG_JOIN_BEVEL: join_type = jtSquare; break;
case NSVGlineJoin::NSVG_JOIN_MITER: join_type = jtMiter; break;
case NSVGlineJoin::NSVG_JOIN_ROUND: join_type = jtRound; break;
}
double mitter = shape.miterLimit * param.scale;
if (join_type == ClipperLib::JoinType::jtRound) {
if (join_type == jtRound) {
// mitter is used as ArcTolerance
// http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/ArcTolerance.htm
mitter = std::pow(param.tesselation_tolerance, 1/3.);
}
float stroke_width = static_cast<float>(shape.strokeWidth * param.scale);
ClipperLib::EndType end_type = ClipperLib::EndType::etOpenButt;
EndType end_type = etOpenButt;
switch (static_cast<NSVGlineCap>(shape.strokeLineCap)) {
case NSVGlineCap::NSVG_CAP_BUTT: end_type = ClipperLib::EndType::etOpenButt; break;
case NSVGlineCap::NSVG_CAP_ROUND: end_type = ClipperLib::EndType::etOpenRound; break;
case NSVGlineCap::NSVG_CAP_SQUARE: end_type = ClipperLib::EndType::etOpenSquare; break;
case NSVGlineCap::NSVG_CAP_BUTT: end_type = etOpenButt; break;
case NSVGlineCap::NSVG_CAP_ROUND: end_type = etOpenRound; break;
case NSVGlineCap::NSVG_CAP_SQUARE: end_type = etOpenSquare; break;
}
Polygons result;
+29 -37
View File
@@ -2,6 +2,7 @@
#include "AABBTreeLines.hpp"
#include "BridgeDetector.hpp"
#include "ClipperUtils.hpp"
#include "ClipperZUtils.hpp"
#include "ExtrusionEntity.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "Feature/FuzzySkin/FuzzySkin.hpp"
@@ -296,13 +297,12 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
return out;
}
static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, const ClipperLib_Z::Paths& clip, ClipperLib_Z::ClipType clipType)
static ClipperZUtils::ZPaths clip_extrusion(const ClipperZUtils::ZPath& subject, const ClipperZUtils::ZPaths& clip, ClipType clipType)
{
ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot,
const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
ClipperLib_Z::IntPoint start = e1bot;
ClipperLib_Z::IntPoint end = e1top;
auto zfill = [](const ClipperZUtils::ZPoint& e1bot, const ClipperZUtils::ZPoint& e1top, const ClipperZUtils::ZPoint& e2bot,
const ClipperZUtils::ZPoint& e2top, ClipperZUtils::ZPoint& pt) {
ClipperZUtils::ZPoint start = e1bot;
ClipperZUtils::ZPoint end = e1top;
if (start.z() <= 0 && end.z() <= 0) {
start = e2bot;
@@ -317,23 +317,15 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, con
double t = std::sqrt(dist_sqr / length_sqr);
pt.z() = start.z() + coord_t((end.z() - start.z()) * t);
});
};
clipper.AddPath(subject, ClipperLib_Z::ptSubject, false);
clipper.AddPaths(clip, ClipperLib_Z::ptClip, true);
ClipperLib_Z::Paths clipped_paths;
{
ClipperLib_Z::PolyTree clipped_polytree;
clipper.Execute(clipType, clipped_polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(std::move(clipped_polytree), clipped_paths);
}
ClipperZUtils::ZPaths clipped_paths = ClipperZUtils::clip_zpaths(clipType, ClipperZUtils::ZPaths{ subject }, true, clip, zfill);
// Clipped path could contain vertices from the clip with a Z coordinate equal to zero.
// For those vertices, we must assign value based on the subject.
// This happens only in sporadic cases.
for (ClipperLib_Z::Path& path : clipped_paths)
for (ClipperLib_Z::IntPoint& c_pt : path)
for (ClipperZUtils::ZPath& path : clipped_paths)
for (ClipperZUtils::ZPoint& c_pt : path)
if (c_pt.z() == 0) {
// Now we must find the corresponding line on with this point is located and compute line width (Z coordinate).
if (subject.size() <= 2)
@@ -366,8 +358,8 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, con
}
assert([&clipped_paths = std::as_const(clipped_paths)]() -> bool {
for (const ClipperLib_Z::Path& path : clipped_paths)
for (const ClipperLib_Z::IntPoint& pt : path)
for (const ClipperZUtils::ZPath& path : clipped_paths)
for (const ClipperZUtils::ZPoint& pt : path)
if (pt.z() <= 0)
return false;
return true;
@@ -376,7 +368,7 @@ static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, con
return clipped_paths;
}
static double clipper_z_path_length(const ClipperLib_Z::Path &path)
static double clipper_z_path_length(const ClipperZUtils::ZPath &path)
{
double len = 0.;
for (size_t i = 1; i < path.size(); ++ i)
@@ -413,7 +405,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
ExtrusionPaths paths;
// detect overhanging/bridging perimeters
if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) {
ClipperLib_Z::Path extrusion_path;
ClipperZUtils::ZPath extrusion_path;
extrusion_path.reserve(extrusion->size());
BoundingBox extrusion_path_bbox;
for (const Arachne::ExtrusionJunction &ej : extrusion->junctions) {
@@ -421,7 +413,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
extrusion_path_bbox.merge(Point(ej.p.x(), ej.p.y()));
}
ClipperLib_Z::Paths lower_slices_paths;
ClipperZUtils::ZPaths lower_slices_paths;
{
lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size());
Points clipped;
@@ -431,7 +423,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
ClipperUtils::clip_clipper_polygon_with_subject_bbox(poly.points, extrusion_path_bbox, clipped);
if (!clipped.empty()) {
lower_slices_paths.emplace_back();
ClipperLib_Z::Path &out = lower_slices_paths.back();
ClipperZUtils::ZPath &out = lower_slices_paths.back();
out.reserve(clipped.size());
for (const Point &pt : clipped)
out.emplace_back(pt.x(), pt.y(), 0);
@@ -440,7 +432,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
}
// get non-overhang paths by intersecting this loop with the grown lower slices
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ctIntersection), role,
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
// Always reverse extrusion if use fuzzy skin: https://github.com/OrcaSlicer/OrcaSlicer/pull/2413#issuecomment-1769735357
@@ -481,7 +473,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
// get overhang paths by checking what parts of this loop fall
// outside the grown lower slices (thus where the distance between
// the loop centerline and original lower slices is >= half nozzle diameter
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), erOverhangPerimeter,
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ctDifference), erOverhangPerimeter,
perimeter_generator.overhang_flow);
// Reapply the nearest point search for starting point.
@@ -674,10 +666,10 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
};
// Pull the cut back by half a wall width: the clip severs the centerline, but the bead's rounded end
// extends half a width past its endpoint and would otherwise overlap the top fill.
ClipperLib_Z::Paths top_paths_z;
ClipperZUtils::ZPaths top_paths_z;
for (const Polygon &poly : to_polygons(offset_ex(top_region, float(perimeter_width) / 2.f))) {
top_paths_z.emplace_back();
ClipperLib_Z::Path &out = top_paths_z.back();
ClipperZUtils::ZPath &out = top_paths_z.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
@@ -695,21 +687,21 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
}
if (overlap == TopOverlap::Full)
continue; // the clip below would return nothing anyway
ClipperLib_Z::Path subject;
ClipperZUtils::ZPath subject;
subject.reserve(el.size());
for (const Arachne::ExtrusionJunction &j : el.junctions)
subject.emplace_back(j.p.x(), j.p.y(), j.w);
ClipperLib_Z::Paths pieces = clip_extrusion(subject, top_paths_z, ClipperLib_Z::ctDifference);
ClipperZUtils::ZPaths pieces = clip_extrusion(subject, top_paths_z, ctDifference);
// Clipper treats the subject as an open polyline, so it also cuts a closed loop at its (arbitrary)
// start vertex and may reverse pieces. Stitch pieces sharing an endpoint back together.
auto same_pt = [](const ClipperLib_Z::IntPoint &p, const ClipperLib_Z::IntPoint &q) {
auto same_pt = [](const ClipperZUtils::ZPoint &p, const ClipperZUtils::ZPoint &q) {
return std::abs(p.x() - q.x()) <= SCALED_EPSILON && std::abs(p.y() - q.y()) <= SCALED_EPSILON;
};
for (size_t i = 0; i < pieces.size(); ++ i) {
for (size_t j = i + 1; j < pieces.size();) {
ClipperLib_Z::Path &a = pieces[i];
ClipperLib_Z::Path &b = pieces[j];
ClipperZUtils::ZPath &a = pieces[i];
ClipperZUtils::ZPath &b = pieces[j];
if (same_pt(a.front(), b.front()) || same_pt(a.front(), b.back()))
std::reverse(a.begin(), a.end());
if (same_pt(a.back(), b.back()))
@@ -726,7 +718,7 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
// If the clip removed next to nothing, keep the loop untouched instead of slitting it open. The
// half-width pull-back above already costs about one width per crossing, hence two widths.
double kept_length = 0.;
for (const ClipperLib_Z::Path &path : pieces)
for (const ClipperZUtils::ZPath &path : pieces)
kept_length += clipper_z_path_length(path);
if (clipper_z_path_length(subject) - kept_length < 2. * double(perimeter_width)) {
append(kept_over_top, covered_by(el));
@@ -734,10 +726,10 @@ static void clip_inner_walls_over_top(std::vector<Arachne::VariableWidthLines> &
continue;
}
for (const ClipperLib_Z::Path &path : pieces) {
for (const ClipperZUtils::ZPath &path : pieces) {
Arachne::ExtrusionLine clipped(el.inset_idx, el.is_odd);
clipped.junctions.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path)
for (const ClipperZUtils::ZPoint &pt : path)
clipped.junctions.emplace_back(Point(pt.x(), pt.y()), coord_t(pt.z()), el.inset_idx);
// Discard tiny leftovers that would print as zits.
if (clipped.size() >= 2 && clipped.getLength() >= perimeter_width)
@@ -1056,7 +1048,7 @@ ExtrusionPaths sort_extra_perimeters(const ExtrusionPaths& extra_perims, int ind
return filtered;
}
#define EXTRA_PERIMETER_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
#define EXTRA_PERIMETER_OFFSET_PARAMETERS jtSquare, 0.
// #define EXTRA_PERIM_DEBUG_FILES
// Function will generate extra perimeters clipped over nonbridgeable areas of the provided surface and returns both the new perimeters and
// Polygons filled by those clipped perimeters
+43 -10
View File
@@ -70,7 +70,7 @@ double Polygon::area() const
bool Polygon::is_counter_clockwise() const
{
return ClipperLib::Orientation(this->points);
return this->area() >= 0.;
}
bool Polygon::is_clockwise() const
@@ -678,7 +678,7 @@ void remove_collinear(Polygons &polys)
remove_collinear(poly);
}
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance, bool strictly_simple /* = true */)
Polygons polygons_simplify(const Polygons &source_polygons, double tolerance)
{
Polygons out;
out.reserve(source_polygons.size());
@@ -687,13 +687,15 @@ Polygons polygons_simplify(const Polygons &source_polygons, double tolerance, bo
Points simplified = MultiPoint::_douglas_peucker(to_polyline(source_polygon).points, tolerance);
// then remove the last (repeated) point.
simplified.pop_back();
// Simplify the decimated contour by ClipperLib.
bool ccw = ClipperLib::Area(simplified) > 0.;
for (Points &path : ClipperLib::SimplifyPolygons(ClipperUtils::SinglePathProvider(simplified), ClipperLib::pftNonZero, strictly_simple)) {
// Simplify the decimated contour by a union.
bool ccw = Polygon::area(simplified) > 0.;
Polygons decimated(1);
decimated.front().points = std::move(simplified);
for (Polygon &polygon : union_(decimated)) {
if (! ccw)
// ClipperLib likely reoriented negative area contours to become positive. Reverse holes back to CW.
std::reverse(path.begin(), path.end());
out.emplace_back(std::move(path));
// The union reorients negative area contours to become positive. Reverse holes back to CW.
polygon.reverse();
out.emplace_back(std::move(polygon));
}
}
return out;
@@ -728,9 +730,40 @@ bool overlaps(const Polygons& polys1, const Polygons& polys2)
return false;
}
// Clipper1's PointInPolygon(): 1 inside, 0 outside, -1 on the boundary.
static int point_in_polygon(const Point &pt, const Points &path)
{
int result = 0;
size_t cnt = path.size();
if (cnt < 3) return 0;
Point ip = path[0];
for (size_t i = 1; i <= cnt; ++i) {
Point ipNext = (i == cnt ? path[0] : path[i]);
if (ipNext.y() == pt.y() && ((ipNext.x() == pt.x()) || (ip.y() == pt.y() && ((ipNext.x() > pt.x()) == (ip.x() < pt.x())))))
return -1;
if ((ip.y() < pt.y()) != (ipNext.y() < pt.y())) {
if (ip.x() >= pt.x()) {
if (ipNext.x() > pt.x())
result = 1 - result;
else {
int64_t d = int64_t(ip.x() - pt.x()) * int64_t(ipNext.y() - pt.y()) - int64_t(ipNext.x() - pt.x()) * int64_t(ip.y() - pt.y());
if (! d) return -1;
if ((d > 0) == (ipNext.y() > ip.y())) result = 1 - result;
}
} else if (ipNext.x() > pt.x()) {
int64_t d = int64_t(ip.x() - pt.x()) * int64_t(ipNext.y() - pt.y()) - int64_t(ipNext.x() - pt.x()) * int64_t(ip.y() - pt.y());
if (! d) return -1;
if ((d > 0) == (ipNext.y() > ip.y())) result = 1 - result;
}
}
ip = ipNext;
}
return result;
}
bool contains(const Polygon &polygon, const Point &p, bool border_result)
{
if (const int poly_count_inside = ClipperLib::PointInPolygon(p, polygon.points);
if (const int poly_count_inside = point_in_polygon(p, polygon.points);
poly_count_inside == -1)
return border_result;
else
@@ -741,7 +774,7 @@ bool contains(const Polygons &polygons, const Point &p, bool border_result)
{
int poly_count_inside = 0;
for (const Polygon &poly : polygons) {
const int is_inside_this_poly = ClipperLib::PointInPolygon(p, poly.points);
const int is_inside_this_poly = point_in_polygon(p, poly.points);
if (is_inside_this_poly == -1)
return border_result;
poly_count_inside += is_inside_this_poly;
+1 -1
View File
@@ -159,7 +159,7 @@ inline void polygons_append(Polygons &dst, Polygons &&src)
}
}
Polygons polygons_simplify(const Polygons &polys, double tolerance, bool strictly_simple = true);
Polygons polygons_simplify(const Polygons &polys, double tolerance);
inline void polygons_rotate(Polygons &polys, double angle)
{
+3 -3
View File
@@ -3489,7 +3489,7 @@ void Print::_make_skirt()
Polygon loop;
{
// Orca: the hull already represents the occupied outline used for this skirt.
Polygons loops = offset(hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
Polygons loops = offset(hull, distance, jtRound, float(scale_(0.1)));
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty())
break;
@@ -3526,7 +3526,7 @@ void Print::_make_skirt()
}
if (collect_skirt_hull)
for (Polygon &poly : offset(hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
for (Polygon &poly : offset(hull, distance + 0.5f * float(scale_(spacing)), jtRound, float(scale_(0.1))))
append(m_skirt_convex_hull, std::move(poly.points));
};
@@ -3632,7 +3632,7 @@ void Print::_make_skirt()
if (group.emits_skirt) {
// Orca: If the expanded skirt outline touches another group
// or obstacle, merge them and run the pass again.
Polygons envelopes = offset(envelope, grouping_offset, ClipperLib::jtRound, float(scale_(0.1)));
Polygons envelopes = offset(envelope, grouping_offset, jtRound, float(scale_(0.1)));
if (envelopes.empty())
continue;
envelope = std::move(envelopes.front());
+2 -2
View File
@@ -251,9 +251,9 @@ enum class PrintOrder
enum class SlicingMode
{
// Regular, applying ClipperLib::pftNonZero rule when creating ExPolygons.
// Regular, applying pftNonZero rule when creating ExPolygons.
Regular,
// Compatible with 3DLabPrint models, applying ClipperLib::pftEvenOdd rule when creating ExPolygons.
// Compatible with 3DLabPrint models, applying pftEvenOdd rule when creating ExPolygons.
EvenOdd,
// Orienting all contours CCW, thus closing all holes.
CloseHoles,
+7 -8
View File
@@ -4,7 +4,6 @@
#include "Print.hpp"
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Clipper2Utils.hpp"
#include "ElephantFootCompensation.hpp"
#include "Geometry.hpp"
#include "I18N.hpp"
@@ -56,7 +55,7 @@ using namespace std::literals;
// #define PRINT_OBJECT_TIMING
#ifdef PRINT_OBJECT_TIMING
// time limit for one ClipperLib operation (union / diff / offset), in ms
// time limit for one Clipper operation (union / diff / offset), in ms
#define PRINT_OBJECT_TIME_LIMIT_DEFAULT 50
#include <boost/current_function.hpp>
#include "Timer.hpp"
@@ -1851,7 +1850,7 @@ void PrintObject::detect_surfaces_type()
// Grow, then keep only what the configured direction allows, using the top's own filled
// outline (same outer edge, holes closed) to tell the two apart.
ExPolygons expanded = offset_ex_2(island_top, d, Clipper2Lib::JoinType::Miter);
ExPolygons expanded = offset_ex(island_top, float(d), jtMiter, 2.);
if (direction != TopSurfaceExpansionDirection::InwardAndOutward) {
ExPolygons outline;
outline.reserve(island_top.size());
@@ -2558,9 +2557,9 @@ void PrintObject::discover_vertical_shells()
// Open to remove (filter out) regions narrower than an infill extrusion line width.
-narrow_ensure_vertical_wall_thickness_region_radius,
// Then close gaps narrower than 1.2 * line width, such gaps are difficult to fill in with sparse infill.
narrow_ensure_vertical_wall_thickness_region_radius + narrow_sparse_infill_region_radius, ClipperLib::jtSquare),
narrow_ensure_vertical_wall_thickness_region_radius + narrow_sparse_infill_region_radius, jtSquare),
// Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions.
narrow_sparse_infill_region_radius - tiny_overlap_radius, ClipperLib::jtSquare);
narrow_sparse_infill_region_radius - tiny_overlap_radius, jtSquare);
Polygons object_volume;
Polygons internal_volume;
@@ -4170,7 +4169,7 @@ void PrintObject::clip_fill_surfaces()
upper_internal = intersection(
// Regularize the overhang regions, so that the infill areas will not become excessively jagged.
smooth_outward(
closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.),
closing(upper_internal, closing_radius, jtSquare, 0.),
scaled<coord_t>(0.1)),
lower_layer_internal_surfaces);
// Apply new internal infill to regions.
@@ -4341,7 +4340,7 @@ void PrintObject::discover_horizontal_shells()
// have the same angle, so the next shell would be grown even more and so on.
Polygons too_narrow = diff(
new_internal_solid,
opening(new_internal_solid, margin, margin + ClipperSafetyOffset, ClipperLib::jtMiter, 5));
opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5));
if (! too_narrow.empty()) {
// grow the collapsing parts and add the extra area to the neighbor layer
// as well as to our original surfaces so that we support this
@@ -4543,7 +4542,7 @@ void PrintObject::_generate_support_material()
}
// BBS
#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
#define SUPPORT_SURFACES_OFFSET_PARAMETERS jtSquare, 0.
#define SUPPORT_MATERIAL_MARGIN 1.2
template<typename PolysType>
void PrintObject::remove_bridges_from_contacts(
+1 -1
View File
@@ -135,7 +135,7 @@ ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta)
Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta)
{
auto arc_tolerance = scaled<double>(0.01);
Polygons res = closing(hull.polygons(), 2 * delta, delta, ClipperLib::jtRound, arc_tolerance);
Polygons res = closing(hull.polygons(), 2 * delta, delta, jtRound, arc_tolerance);
auto it = std::remove_if(res.begin(), res.end(), [](Polygon &p) { return p.is_clockwise(); });
res.erase(it, res.end());
+2 -18
View File
@@ -171,24 +171,8 @@ struct PadSkeleton { ExPolygons inner, outer; };
PadSkeleton divide_blueprint(const ExPolygons &bp)
{
ClipperLib::PolyTree ptree = union_pt(bp);
PadSkeleton ret;
ret.inner.reserve(size_t(ptree.Total()));
ret.outer.reserve(size_t(ptree.Total()));
for (ClipperLib::PolyTree::PolyNode *node : ptree.Childs) {
ExPolygon poly;
poly.contour.points = std::move(node->Contour);
for (ClipperLib::PolyTree::PolyNode *child : node->Childs) {
poly.holes.emplace_back(std::move(child->Contour));
traverse_pt(child->Childs, &ret.inner);
}
ret.outer.emplace_back(poly);
}
ret.outer = top_level_expolygons(bp, &ret.inner);
return ret;
}
@@ -257,7 +241,7 @@ public:
auto model_bp_offs =
offset_ex(model_blueprint,
scaled<float>(cfg.embed_object.object_gap_mm),
ClipperLib::jtMiter, 1);
jtMiter, 1);
ExPolygons fullcvh =
wafflized_concave_hull(support_blueprint, model_bp_offs, cfg, thr);
+2 -2
View File
@@ -186,8 +186,8 @@ static std::vector<SupportPointGenerator::MyLayer> make_layers(
// Produce 2 bands around the island, a safe band for dangling overhangs
// and an unsafe band for sloped overhangs.
// These masks include the original island
auto dangl_mask = expand(bottom_polygons, between_layers_offset, ClipperLib::jtSquare);
auto overh_mask = expand(bottom_polygons, slope_offset, ClipperLib::jtSquare);
auto dangl_mask = expand(bottom_polygons, between_layers_offset, jtSquare);
auto overh_mask = expand(bottom_polygons, slope_offset, jtSquare);
// Absolutely hopeless overhangs are those outside the unsafe band
top.overhangs = diff_ex(*top.polygon, overh_mask);
+5 -5
View File
@@ -230,15 +230,15 @@ void SVG::draw(const Points &points, std::string fill, coord_t radius)
this->draw(*it, fill, radius);
}
void SVG::draw(const ClipperLib::Path &polygon, double scale, std::string stroke, coordf_t stroke_width)
void SVG::draw(const Points &polygon, double scale, std::string stroke, coordf_t stroke_width)
{
this->stroke = stroke;
this->path(this->get_path_d(polygon, scale, true), false, stroke_width, 1.f);
}
void SVG::draw(const ClipperLib::Paths &polygons, double scale, std::string stroke, coordf_t stroke_width)
void SVG::draw(const VecOfPoints &polygons, double scale, std::string stroke, coordf_t stroke_width)
{
for (ClipperLib::Paths::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
for (VecOfPoints::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
draw(*it, scale, stroke, stroke_width);
}
@@ -284,11 +284,11 @@ std::string SVG::get_path_d(const MultiPoint &mp, bool closed) const
return d.str();
}
std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const
std::string SVG::get_path_d(const Points &path, double scale, bool closed) const
{
std::ostringstream d;
d << "M ";
for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
for (Points::const_iterator p = path.begin(); p != path.end(); ++p) {
d << to_svg_x(scale * p->x() - origin(0)) << " ";
d << to_svg_y(scale * p->y() - origin(1)) << " ";
}
+4 -5
View File
@@ -2,7 +2,6 @@
#define slic3r_SVG_hpp_
#include "libslic3r.h"
#include "clipper.hpp"
#include "ExPolygon.hpp"
#include "Line.hpp"
#include "TriangleMesh.hpp"
@@ -70,9 +69,9 @@ public:
void draw(const Point &point, std::string fill = "black", coord_t radius = 0);
void draw(const Points &points, std::string fill = "black", coord_t radius = 0);
// Support for rendering the ClipperLib paths
void draw(const ClipperLib::Path &polygon, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
void draw(const ClipperLib::Paths &polygons, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
// Paths drawn with their coordinates multiplied by scale.
void draw(const Points &polygon, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
void draw(const VecOfPoints &polygons, double scale, std::string fill = "grey", coordf_t stroke_width = 0);
void draw_text(const Point &pt, const char *text, const char *color, int font_size = 20);
void draw_legend(const Point &pt, const char *text, const char *color);
@@ -88,7 +87,7 @@ public:
void path(const std::string &d, bool fill, coordf_t stroke_width, const float fill_opacity);
std::string get_path_d(const MultiPoint &mp, bool closed = false) const;
std::string get_path_d(const ClipperLib::Path &mp, double scale, bool closed = false) const;
std::string get_path_d(const Points &mp, double scale, bool closed = false) const;
public:
static void export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0);
-17
View File
@@ -4,7 +4,6 @@
#undef assert
#endif
#include "clipper.hpp"
#include "ShortestPath.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "KDTreeIndirect.hpp"
@@ -2041,22 +2040,6 @@ Polylines chain_polylines(Polylines &&polylines, const Point *start_near)
return out;
}
template<class T> static inline T chain_path_items(const Points &points, const T &items)
{
auto segment_end_point = [&points](size_t idx, bool /* first_point */) -> const Point& { return points[idx]; };
std::vector<std::pair<size_t, bool>> ordered = chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, points.size(), nullptr);
T out;
out.reserve(items.size());
for (auto &segment_and_reversal : ordered)
out.emplace_back(items[segment_and_reversal.first]);
return out;
}
ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items)
{
return chain_path_items(points, items);
}
// BBS
std::vector<const PrintInstance*> chain_print_object_instances(const std::vector<const PrintObject*>& print_objects, const Point* start_near)
{
-6
View File
@@ -11,11 +11,6 @@
namespace Slic3r {
namespace ClipperLib {
class PolyNode;
using PolyNodes = std::vector<PolyNode*, PointsAllocator<PolyNode*>>;
}
std::vector<size_t> chain_points(const Points &points, const Point *start_near = nullptr);
// Variant with post-processing (crossing removal + 2-opt) for object ordering.
std::vector<size_t> chain_points_with_postprocessing(const Points &points, const Point *start_near = nullptr);
@@ -49,7 +44,6 @@ template<typename T> inline void reorder_by_shortest_traverse(std::vector<T> &po
for (size_t i:order) polylines_out.emplace_back(std::move(Temp[i]));
}
ClipperLib::PolyNodes chain_clipper_polynodes(const Points &points, const ClipperLib::PolyNodes &items);
// Chain instances of print objects by an approximate shortest path.
// Returns pairs of PrintObject idx and instance of that PrintObject.
+41 -69
View File
@@ -1,5 +1,5 @@
#include "../ClipperUtils.hpp"
// #include "../ClipperZUtils.hpp"
#include "../ClipperZUtils.hpp"
#include "../ExtrusionEntityCollection.hpp"
#include "../Layer.hpp"
#include "../Print.hpp"
@@ -7,7 +7,6 @@
#include "../MutablePolygon.hpp"
#include "../Geometry.hpp"
#include "../Point.hpp"
#include "clipper/clipper_z.hpp"
#include <cmath>
#include <boost/container/static_vector.hpp>
@@ -39,9 +38,9 @@ namespace Slic3r {
//FIXME this should be dependent on the nozzle diameter!
#define SUPPORT_MATERIAL_MARGIN 1.5
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS jtMiter, 3.
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS jtMiter, 1.5
#define SUPPORT_SURFACES_OFFSET_PARAMETERS jtSquare, 0.
// Convert some of the intermediate layers into top/bottom interface layers as well as base interface layers.
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
@@ -280,13 +279,13 @@ SupportGeneratorLayersPtr generate_raft_base(
polygons_append(brim, offset(ex, brim_object_gap));
else {
if (brim_outer)
polygons_append(brim, offset(ex.contour, brim_object_gap, ClipperLib::jtRound, float(scale_(0.1))));
polygons_append(brim, offset(ex.contour, brim_object_gap, jtRound, float(scale_(0.1))));
else
brim.emplace_back(ex.contour);
if (brim_inner) {
Polygons holes = ex.holes;
polygons_reverse(holes);
holes = shrink(holes, brim_object_gap, ClipperLib::jtRound, float(scale_(0.1)));
holes = shrink(holes, brim_object_gap, jtRound, float(scale_(0.1)));
polygons_reverse(holes);
polygons_append(brim, std::move(holes));
} else
@@ -493,32 +492,25 @@ void tree_supports_generate_paths(
// Offset expolygon inside, returns number of expolygons collected (0 or 1).
// Vertices of output paths are marked with Z = source contour index of the expoly.
// Vertices at the intersection of source contours are marked with Z = -1.
auto shrink_expolygon_with_contour_idx = [](const Slic3r::ExPolygon &expoly, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib_Z::Paths &out) -> int
auto shrink_expolygon_with_contour_idx = [](const Slic3r::ExPolygon &expoly, const float delta, JoinType joinType, double miterLimit, ClipperZUtils::ZPaths &out) -> int
{
assert(delta > 0);
auto append_paths_with_z = [](ClipperLib::Paths &src, coord_t contour_idx, ClipperLib_Z::Paths &dst) {
auto append_paths_with_z = [](const Polygons &src, coord_t contour_idx, ClipperZUtils::ZPaths &dst) {
dst.reserve(next_highest_power_of_2(dst.size() + src.size()));
for (const ClipperLib::Path &contour : src) {
ClipperLib_Z::Path tmp;
tmp.reserve(contour.size());
for (const Point &p : contour)
tmp.emplace_back(p.x(), p.y(), contour_idx);
dst.emplace_back(std::move(tmp));
}
for (const Polygon &contour : src)
dst.emplace_back(ClipperZUtils::to_zpath(contour.points, contour_idx));
};
// Oriented CCW, the sign of d alone decides between growing and shrinking.
auto offset_ccw = [joinType, miterLimit](Polygon polygon, float d) {
if (! polygon.is_counter_clockwise())
polygon.reverse();
return offset(polygon, d, joinType, miterLimit);
};
// 1) Offset the outer contour.
ClipperLib_Z::Paths contours;
ClipperZUtils::ZPaths contours;
{
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
co.ArcTolerance = miterLimit;
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(delta * 0.005);
co.AddPath(expoly.contour.points, joinType, ClipperLib::etClosedPolygon);
ClipperLib::Paths contours_raw;
co.Execute(contours_raw, - delta);
Polygons contours_raw = offset_ccw(expoly.contour, - delta);
if (contours_raw.empty())
// No need to try to offset the holes.
return 0;
@@ -530,24 +522,9 @@ void tree_supports_generate_paths(
append(out, std::move(contours));
} else {
// 2) Offset the holes one by one, collect the offsetted holes.
ClipperLib_Z::Paths holes;
{
for (const Polygon &hole : expoly.holes) {
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
co.ArcTolerance = miterLimit;
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(delta * 0.005);
co.AddPath(hole.points, joinType, ClipperLib::etClosedPolygon);
ClipperLib::Paths out2;
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output
// contours will be CCW oriented even though the input paths are CW oriented.
// Offset is applied after contour reorientation, thus the signum of the offset value is reversed.
co.Execute(out2, delta);
append_paths_with_z(out2, 1 + (&hole - expoly.holes.data()), holes);
}
}
ClipperZUtils::ZPaths holes;
for (const Polygon &hole : expoly.holes)
append_paths_with_z(offset_ccw(hole, delta), 1 + (&hole - expoly.holes.data()), holes);
// 3) Subtract holes from the contours.
if (holes.empty()) {
@@ -556,16 +533,11 @@ void tree_supports_generate_paths(
} else {
// Negative offset. There is a chance, that the offsetted hole intersects the outer contour.
// Subtract the offsetted holes from the offsetted contours.
ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
//pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
ClipperZUtils::ZPaths output = ClipperZUtils::clip_zpaths(ctDifference, contours, false, holes,
[](const ClipperZUtils::ZPoint &, const ClipperZUtils::ZPoint &, const ClipperZUtils::ZPoint &, const ClipperZUtils::ZPoint &, ClipperZUtils::ZPoint &pt) {
// Just mark the intersection.
pt.z() = -1;
});
clipper.AddPaths(contours, ClipperLib_Z::ptSubject, true);
clipper.AddPaths(holes, ClipperLib_Z::ptClip, true);
ClipperLib_Z::Paths output;
clipper.Execute(ClipperLib_Z::ctDifference, output, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
if (! output.empty()) {
append(out, std::move(output));
} else {
@@ -582,7 +554,7 @@ void tree_supports_generate_paths(
// Clip the sheath path to avoid the extruder to get exactly on the first point of the loop.
const double clip_length = spacing * 0.15;
const double anchor_length = spacing * 6.;
ClipperLib_Z::Paths anchor_candidates;
ClipperZUtils::ZPaths anchor_candidates;
for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) {
std::unique_ptr<ExtrusionEntityCollection> eec;
ExPolygons regions_to_draw_inner_wall{expoly};
@@ -608,10 +580,10 @@ void tree_supports_generate_paths(
// First genrate a 2nd perimeter loop as a source for anchor candidates.
// The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection.
anchor_candidates.clear();
shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates);
shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), jtMiter, 1.2, anchor_candidates);
// Orient all contours CW.
for (auto &path : anchor_candidates)
if (ClipperLib_Z::Area(path) > 0) std::reverse(path.begin(), path.end());
if (ClipperZUtils::area(path) > 0) std::reverse(path.begin(), path.end());
// Draw the perimeters.
Polylines polylines;
@@ -628,13 +600,13 @@ void tree_supports_generate_paths(
pl.clip_end(clip_length);
if (pl.size() < 2) continue;
// Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour.
ClipperLib_Z::Path *closest_contour = nullptr;
Vec2d closest_point;
int closest_point_idx = -1;
double closest_point_t = 0.;
double d2min = std::numeric_limits<double>::max();
Vec2d seam_pt = pl.back().cast<double>();
for (ClipperLib_Z::Path &path : anchor_candidates)
ClipperZUtils::ZPath *closest_contour = nullptr;
Vec2d closest_point;
int closest_point_idx = -1;
double closest_point_t = 0.;
double d2min = std::numeric_limits<double>::max();
Vec2d seam_pt = pl.back().cast<double>();
for (ClipperZUtils::ZPath &path : anchor_candidates)
for (int i = 0; i < int(path.size()); ++i) {
int j = next_idx_modulo(i, path);
if (path[i].z() == idx_loop || path[j].z() == idx_loop) {
@@ -662,14 +634,14 @@ void tree_supports_generate_paths(
// Try to cut an anchor from the closest_contour.
// Both closest_contour and pl are CW oriented.
pl.points.emplace_back(closest_point.cast<coord_t>());
const ClipperLib_Z::Path &path = *closest_contour;
double remaining_length = anchor_length - (seam_pt - closest_point).norm();
int i = closest_point_idx;
int j = next_idx_modulo(i, *closest_contour);
Vec2d pi(path[i].x(), path[i].y());
Vec2d pj(path[j].x(), path[j].y());
Vec2d v = pj - pi;
double l = v.norm();
const ClipperZUtils::ZPath &path = *closest_contour;
double remaining_length = anchor_length - (seam_pt - closest_point).norm();
int i = closest_point_idx;
int j = next_idx_modulo(i, *closest_contour);
Vec2d pi(path[i].x(), path[i].y());
Vec2d pj(path[j].x(), path[j].y());
Vec2d v = pj - pi;
double l = v.norm();
if (remaining_length < (1. - closest_point_t) * l) {
// Just trim the current line.
pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast<coord_t>());
+4 -4
View File
@@ -63,9 +63,9 @@ namespace Slic3r {
#define PILLAR_SIZE (2.5)
#define PILLAR_SPACING 10
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS jtMiter, 3.
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS jtMiter, 1.5
#define SUPPORT_SURFACES_OFFSET_PARAMETERS jtSquare, 0.
static constexpr bool support_with_sheath = false;
@@ -1634,7 +1634,7 @@ static inline std::tuple<Polygons, Polygons, double> detect_contacts(
offset(
diff_polygons,
scaled<float>(SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS),
ClipperLib::jtRound,
jtRound,
// round mitter limit
scale_(0.05)),
slices_margin.polygons);
+12 -12
View File
@@ -112,7 +112,7 @@ TreeModelVolumes::TreeModelVolumes(
tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers, num_layers, std::min<size_t>(1, std::max<size_t>(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))),
[&](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
outlines[layer_idx] = polygons_simplify(to_polygons(print_object.get_layer(layer_idx - num_raft_layers)->lslices), mesh_settings.resolution, polygons_strictly_simple);
outlines[layer_idx] = polygons_simplify(to_polygons(print_object.get_layer(layer_idx - num_raft_layers)->lslices), mesh_settings.resolution);
});
}
#endif
@@ -462,7 +462,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
// if a key does not exist when it is accessed it is added!
collision_areas_offsetted[layer_idx] = offset_value == 0 ?
union_(collision_areas) :
offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2);
offset(union_ex(collision_areas), offset_value, jtMiter, 1.2);
if(throw_on_cancel)
throw_on_cancel();
}
@@ -515,16 +515,16 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
// the conditional -0.5 ensures that plastic can never touch on the diagonal
// downward when the z_distance_top_layers = 1. It is assumed to be better to
// not support an overhang<90 degree than to risk fusing to it.
append(collisions, offset(union_ex(collision_areas_original), radius + required_range_x, ClipperLib::jtMiter, 1.2));
append(collisions, offset(union_ex(collision_areas_original), radius + required_range_x, jtMiter, 1.2));
}
collisions = processing_last_mesh && layer_idx < int(anti_overhang.size()) ?
union_(collisions, offset(union_ex(anti_overhang[layer_idx]), radius, ClipperLib::jtMiter, 1.2)) :
union_(collisions, offset(union_ex(anti_overhang[layer_idx]), radius, jtMiter, 1.2)) :
union_(collisions);
auto &dst = data[layer_idx];
if (processing_last_mesh) {
if (! dst.empty())
collisions = union_(collisions, dst);
dst = polygons_simplify(collisions, min_resolution, polygons_strictly_simple);
dst = polygons_simplify(collisions, min_resolution);
} else
append(dst, std::move(collisions));
if (throw_on_cancel)
@@ -552,7 +552,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
if (processing_last_mesh) {
if (! dst.empty())
placable = union_(placable, dst);
dst = polygons_simplify(placable, min_resolution, polygons_strictly_simple);
dst = polygons_simplify(placable, min_resolution);
} else
append(dst, placable);
if (throw_on_cancel)
@@ -601,8 +601,8 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vector<RadiusLayerP
// this union is important as otherwise holes(in form of lines that will increase to holes in a later step) can get unioned onto the area.
data.emplace_back(RadiusLayerPair(radius, layer_idx), polygons_simplify(
offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)),
5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution),
m_min_resolution, polygons_strictly_simple));
5 - increase_radius_ceil, jtRound, m_min_resolution),
m_min_resolution));
if (throw_on_cancel)
throw_on_cancel();
}
@@ -689,10 +689,10 @@ void TreeModelVolumes::calculateAvoidance(const std::vector<RadiusLayerPair> &ke
latest_avoidance = union_(current_layer_collisions,
offset(latest_avoidance,
istep + 1 == move_steps ? - last_move_step : - move_step,
ClipperLib::jtRound, m_min_resolution));
jtRound, m_min_resolution));
if (task.to_model)
latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel));
latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution, polygons_strictly_simple);
latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution);
data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance);
if (throw_on_cancel)
throw_on_cancel();
@@ -815,12 +815,12 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector<RadiusLayerPa
data[layer_idx - min_layer_bottom] = polygons_simplify(
// radius contains m_current_min_xy_dist_delta already if required
intersection(getCollision(0, layer_idx, false), getCollision(radius, layer_idx - 1, true)),
m_min_resolution, polygons_strictly_simple);
m_min_resolution);
if (! data_min.empty())
data_min[layer_idx - min_layer_bottom] =
polygons_simplify(
intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)),
m_min_resolution, polygons_strictly_simple);
m_min_resolution);
if (throw_on_cancel)
throw_on_cancel();
}
+3 -3
View File
@@ -664,7 +664,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
}
#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
#define SUPPORT_SURFACES_OFFSET_PARAMETERS jtSquare, 0.
void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
{
bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value);
@@ -2571,7 +2571,7 @@ void TreeSupport::draw_circles()
Point direction = normal(pt_on_expoly - pt_on_poly, line_width_scaled / 2);
hole_lower.translate(direction);
// note to expand a hole, we need to do negative offset
auto hole_expanded = offset(hole_lower, -line_width_scaled / 4, ClipperLib::JoinType::jtSquare);
auto hole_expanded = offset(hole_lower, -line_width_scaled / 4, jtSquare);
if (!hole_expanded.empty()) {
base_area_lower.holes.push_back(std::move(hole_expanded[0]));
holePropagationInfos.insert({ &base_area_lower.holes.back(), {25, direction, pt_far_on_poly} });
@@ -2584,7 +2584,7 @@ void TreeSupport::draw_circles()
auto&& direction = std::get<1>(holePropagationInfos[&hole]);
hole_lower.translate(direction);
// note to shrink a hole, we need to do positive offset
auto hole_expanded = offset(hole_lower, line_width_scaled / 2, ClipperLib::JoinType::jtSquare);
auto hole_expanded = offset(hole_lower, line_width_scaled / 2, jtSquare);
Point farPoint = std::get<2>(holePropagationInfos[&hole]) + direction * 2;
if (!hole_expanded.empty()) {
base_area_lower.holes.push_back(std::move(hole_expanded[0]));
+8 -8
View File
@@ -68,7 +68,7 @@ static inline void validate_range(const Point &pt)
{
static constexpr const int32_t hi = 65536 * 16384;
if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi)
throw ClipperLib::clipperException("Coordinate outside allowed range");
throw RuntimeError("Coordinate outside allowed range");
}
static inline void validate_range(const Points &points)
@@ -832,16 +832,16 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
}
// offset in steps
for (int i = 0; i < steps; ++ i) {
ret = diff(offset(ret, step_size, ClipperLib::jtRound, scaled<float>(0.01)), collision_trimmed());
ret = diff(offset(ret, step_size, jtRound, scaled<float>(0.01)), collision_trimmed());
// ensure that if many offsets are done the performance does not suffer extremely by the new vertices of jtRound.
if (i % 10 == 7)
ret = polygons_simplify(ret, scaled<double>(0.015), polygons_strictly_simple);
ret = polygons_simplify(ret, scaled<double>(0.015));
}
// offset the remainder
float last_offset = distance - steps * step_size;
if (last_offset > SCALED_EPSILON)
ret = offset(ret, distance - steps * step_size, ClipperLib::jtRound, scaled<float>(0.01));
ret = polygons_simplify(ret, scaled<double>(0.015), polygons_strictly_simple);
ret = offset(ret, distance - steps * step_size, jtRound, scaled<float>(0.01));
ret = polygons_simplify(ret, scaled<double>(0.015));
if (do_final_difference)
ret = diff(ret, collision_trimmed());
@@ -1622,8 +1622,8 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
safe_movement_distance, safe_movement_distance + radius, 1);
}
if (settings.no_error && settings.move)
// as ClipperLib::jtRound has to be used for offsets this simplify is VERY important for performance.
polygons_simplify(increased, scaled<float>(0.025), polygons_strictly_simple);
// as jtRound has to be used for offsets this simplify is VERY important for performance.
polygons_simplify(increased, scaled<float>(0.025));
} else
// if no movement is done the areas keep parent area as no move == offset(0)
increased = parent.influence_area;
@@ -4145,7 +4145,7 @@ void organic_draw_branches(
base_layer_polygons = smooth_outward(union_(base_layer_polygons), config.support_line_width); //FIXME was .smooth(50);
//smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
// simplify a bit, to ensure the output does not contain outrageous amounts of vertices. Should not be necessary, just a precaution.
base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled<double>(0.03), double(config.resolution)), polygons_strictly_simple);
base_layer_polygons = polygons_simplify(base_layer_polygons, std::min(scaled<double>(0.03), double(config.resolution)));
}
// Subtract top contact layer polygons from support base.
@@ -588,8 +588,6 @@ private:
// std::vector<coord_t> known_z;
};
static constexpr const bool polygons_strictly_simple = false;
inline double tiny_area_threshold() { return sqr(scaled<double>(0.001)); }
inline void tree_supports_show_error(std::string_view message, bool critical)
+3 -3
View File
@@ -1856,7 +1856,7 @@ static ExPolygons make_expolygons_simple(std::vector<IntersectionLine> &lines)
return slices;
}
static void make_expolygons(const Polygons &loops, const float closing_radius, const float extra_offset, ClipperLib::PolyFillType fill_type, ExPolygons* slices)
static void make_expolygons(const Polygons &loops, const float closing_radius, const float extra_offset, PolyFillType fill_type, ExPolygons* slices)
{
/*
Input loops are not suitable for evenodd nor nonzero fill types, as we might get
@@ -2156,8 +2156,8 @@ std::vector<ExPolygons> slice_mesh_ex(
const auto this_mode = layer_id < params.slicing_mode_normal_below_layer ? params.mode_below : params.mode;
Slic3r::make_expolygons(
layers_p[layer_id], params.closing_radius, params.extra_offset,
this_mode == MeshSlicingParams::SlicingMode::EvenOdd ? ClipperLib::pftEvenOdd :
this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour ? ClipperLib::pftPositive : ClipperLib::pftNonZero,
this_mode == MeshSlicingParams::SlicingMode::EvenOdd ? pftEvenOdd :
this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour ? pftPositive : pftNonZero,
&expolygons);
//FIXME simplify
if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour)
+3 -3
View File
@@ -12,13 +12,13 @@ struct MeshSlicingParams
{
enum class SlicingMode : uint32_t {
// Regular slicing, maintain all contours and their orientation.
// slice_mesh_ex() applies ClipperLib::pftNonZero rule to the result of slice_mesh().
// slice_mesh_ex() applies pftNonZero rule to the result of slice_mesh().
Regular,
// For slicing 3DLabPrints plane models (aka to be compatible with S3D default strategy).
// slice_mesh_ex() applies ClipperLib::pftEvenOdd rule. slice_mesh() slices EvenOdd as Regular.
// slice_mesh_ex() applies pftEvenOdd rule. slice_mesh() slices EvenOdd as Regular.
EvenOdd,
// Maintain all contours, orient all contours CCW.
// slice_mesh_ex() applies ClipperLib::pftNonZero rule, thus holes will be closed.
// slice_mesh_ex() applies pftNonZero rule, thus holes will be closed.
Positive,
// Orient all contours CCW and keep only the contour with the largest area.
// This mode is useful for slicing complex objects in vase mode.
-13
View File
@@ -1,13 +0,0 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library using Slic3r::Point.
#include "clipper.hpp"
// Don't include <clipper/clipper.hpp> for the second time.
#define clipper_hpp
// Override ClipperLib namespace to Slic3r::ClipperLib
#define CLIPPERLIB_NAMESPACE_PREFIX Slic3r
// Override Slic3r::ClipperLib::IntPoint to Slic3r::Point
#define CLIPPERLIB_INTPOINT_TYPE Slic3r::Point
#include <clipper/clipper.cpp>
-26
View File
@@ -1,26 +0,0 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library using Slic3r's own Point type.
#ifndef slic3r_clipper_hpp
#ifdef clipper_hpp
#error "You should include the libslic3r/clipper.hpp before clipper/clipper.hpp"
#endif
#ifdef CLIPPERLIB_USE_XYZ
#error "Something went wrong. Using clipper.hpp with Slic3r Point type, but CLIPPERLIB_USE_XYZ is defined."
#endif
#define slic3r_clipper_hpp
#include "Point.hpp"
#define CLIPPERLIB_NAMESPACE_PREFIX Slic3r
#define CLIPPERLIB_INTPOINT_TYPE Slic3r::Point
#include <clipper/clipper.hpp>
#undef clipper_hpp
#undef CLIPPERLIB_NAMESPACE_PREFIX
#undef CLIPPERLIB_INTPOINT_TYPE
#endif // slic3r_clipper_hpp
-2
View File
@@ -110,8 +110,6 @@
#include <cereal/access.hpp>
#include <cereal/types/base_class.hpp>
#include <clipper/clipper_z.hpp>
#include "clipper.hpp"
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Config.hpp"