::Type`.
@@ -473,8 +474,8 @@ inline _Box _Box
::infinite(const P& center) {
// It is important for Mx and My to be strictly less than half of the
// range of type C. width(), height() and area() will not overflow this way.
- C Mx = C((std::numeric_limits::lowest() + 2 * getX(center)) / 2.01);
- C My = C((std::numeric_limits::lowest() + 2 * getY(center)) / 2.01);
+ C Mx = C((std::numeric_limits::lowest() + 2 * getX(center)) / 4.01);
+ C My = C((std::numeric_limits::lowest() + 2 * getY(center)) / 4.01);
ret.maxCorner() = center - P{Mx, My};
ret.minCorner() = center + P{Mx, My};
diff --git a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp
index 29a1ccd047..6b3ff60c18 100644
--- a/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp
+++ b/src/libnest2d/include/libnest2d/geometry_traits_nfp.hpp
@@ -220,7 +220,9 @@ inline NfpResult nfpConvexOnly(const RawShape& sh,
auto next = std::next(first);
while(next != sl::cend(sh)) {
- edgelist.emplace_back(*(first), *(next));
+ if (pl::magnsq(*next - *first) > 0)
+ edgelist.emplace_back(*(first), *(next));
+
++first; ++next;
}
}
@@ -230,7 +232,9 @@ inline NfpResult nfpConvexOnly(const RawShape& sh,
auto next = std::next(first);
while(next != sl::cend(other)) {
- edgelist.emplace_back(*(next), *(first));
+ if (pl::magnsq(*next - *first) > 0)
+ edgelist.emplace_back(*(next), *(first));
+
++first; ++next;
}
}
diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp
index 5eef28c407..9d24a25696 100644
--- a/src/libnest2d/include/libnest2d/libnest2d.hpp
+++ b/src/libnest2d/include/libnest2d/libnest2d.hpp
@@ -130,6 +130,11 @@ std::size_t nest(Container&& cont,
return nest(cont.begin(), cont.end(), bin, dist, cfg, ctl);
}
+template enable_if_t::value, TCoord> mm(T val = T(1))
+{
+ return TCoord(val * CoordType::MM_IN_COORDS);
+}
+
}
#endif // LIBNEST2D_HPP
diff --git a/src/libnest2d/include/libnest2d/parallel.hpp b/src/libnest2d/include/libnest2d/parallel.hpp
new file mode 100644
index 0000000000..2eaf86c018
--- /dev/null
+++ b/src/libnest2d/include/libnest2d/parallel.hpp
@@ -0,0 +1,63 @@
+#ifndef LIBNEST2D_PARALLEL_HPP
+#define LIBNEST2D_PARALLEL_HPP
+
+#include
+#include
+#include
+
+#ifdef LIBNEST2D_THREADING_tbb
+#include
+#endif
+
+#ifdef LIBNEST2D_THREADING_omp
+#include
+#endif
+
+namespace libnest2d { namespace __parallel {
+
+template
+using TIteratorValue = typename std::iterator_traits::value_type;
+
+template
+inline void enumerate(
+ Iterator from, Iterator to,
+ std::function, size_t)> fn,
+ std::launch policy = std::launch::deferred | std::launch::async)
+{
+ using TN = size_t;
+ auto iN = to-from;
+ TN N = iN < 0? 0 : TN(iN);
+
+#ifdef LIBNEST2D_THREADING_tbb
+ if((policy & std::launch::async) == std::launch::async) {
+ tbb::parallel_for(0, N, [from, fn] (TN n) { fn(*(from + n), n); } );
+ } else {
+ for(TN n = 0; n < N; n++) fn(*(from + n), n);
+ }
+#endif
+
+#ifdef LIBNEST2D_THREADING_omp
+ if((policy & std::launch::async) == std::launch::async) {
+ #pragma omp parallel for
+ for(int n = 0; n < int(N); n++) fn(*(from + n), TN(n));
+ }
+ else {
+ for(TN n = 0; n < N; n++) fn(*(from + n), n);
+ }
+#endif
+
+#ifdef LIBNEST2D_THREADING_std
+ std::vector> rets(N);
+
+ auto it = from;
+ for(TN b = 0; b < N; b++) {
+ rets[b] = std::async(policy, fn, *it++, unsigned(b));
+ }
+
+ for(TN fi = 0; fi < N; ++fi) rets[fi].wait();
+#endif
+}
+
+}}
+
+#endif //LIBNEST2D_PARALLEL_HPP
diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
index 83f7bd246a..bd9c603558 100644
--- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
+++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
@@ -20,59 +20,9 @@
// temporary
//#include "../tools/svgtools.hpp"
-#ifdef USE_TBB
-#include
-#elif defined(_OPENMP)
-#include
-#endif
+#include
namespace libnest2d {
-
-namespace __parallel {
-
-using std::function;
-using std::iterator_traits;
-template
-using TIteratorValue = typename iterator_traits::value_type;
-
-template
-inline void enumerate(
- Iterator from, Iterator to,
- function, size_t)> fn,
- std::launch policy = std::launch::deferred | std::launch::async)
-{
- using TN = size_t;
- auto iN = to-from;
- TN N = iN < 0? 0 : TN(iN);
-
-#ifdef USE_TBB
- if((policy & std::launch::async) == std::launch::async) {
- tbb::parallel_for(0, N, [from, fn] (TN n) { fn(*(from + n), n); } );
- } else {
- for(TN n = 0; n < N; n++) fn(*(from + n), n);
- }
-#elif defined(_OPENMP)
- if((policy & std::launch::async) == std::launch::async) {
- #pragma omp parallel for
- for(int n = 0; n < int(N); n++) fn(*(from + n), TN(n));
- }
- else {
- for(TN n = 0; n < N; n++) fn(*(from + n), n);
- }
-#else
- std::vector> rets(N);
-
- auto it = from;
- for(TN b = 0; b < N; b++) {
- rets[b] = std::async(policy, fn, *it++, unsigned(b));
- }
-
- for(TN fi = 0; fi < N; ++fi) rets[fi].wait();
-#endif
-}
-
-}
-
namespace placers {
template
diff --git a/src/libnest2d/tools/svgtools.hpp b/src/libnest2d/tools/svgtools.hpp
index 2a05b551d1..47757cd46c 100644
--- a/src/libnest2d/tools/svgtools.hpp
+++ b/src/libnest2d/tools/svgtools.hpp
@@ -54,10 +54,10 @@ public:
if(conf_.origo_location == BOTTOMLEFT) {
auto d = static_cast(
std::round(conf_.height*conf_.mm_in_coord_units) );
-
+
auto& contour = shapelike::contour(tsh);
for(auto& v : contour) setY(v, -getY(v) + d);
-
+
auto& holes = shapelike::holes(tsh);
for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
@@ -81,6 +81,18 @@ public:
finishLayer();
}
}
+
+ template void writeItems(ItemIt from, ItemIt to) {
+ auto it = from;
+ PackGroup pg;
+ while(it != to) {
+ if(it->binId() == BIN_ID_UNSET) continue;
+ while(pg.size() <= size_t(it->binId())) pg.emplace_back();
+ pg[it->binId()].emplace_back(*it);
+ ++it;
+ }
+ writePackGroup(pg);
+ }
void addLayer() {
svg_layers_.emplace_back(header());
diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp
index b11c570f66..76aa36194e 100644
--- a/src/libslic3r/AABBTreeIndirect.hpp
+++ b/src/libslic3r/AABBTreeIndirect.hpp
@@ -11,6 +11,8 @@
#include
#include
+#include
+
#include "Utils.hpp" // for next_highest_power_of_2()
extern "C"
@@ -752,6 +754,83 @@ void get_candidate_idxs(const TreeType& tree, const VectorType& v, std::vector struct Intersecting {};
+
+// Intersection predicate specialization for box-box intersections
+template
+struct Intersecting> {
+ Eigen::AlignedBox box;
+
+ Intersecting(const Eigen::AlignedBox &bb): box{bb} {}
+
+ bool operator() (const typename Tree::Node &node) const
+ {
+ return box.intersects(node.bbox);
+ }
+};
+
+template auto intersecting(const G &g) { return Intersecting{g}; }
+
+template struct Containing {};
+
+// Intersection predicate specialization for box-box intersections
+template
+struct Containing> {
+ Eigen::AlignedBox box;
+
+ Containing(const Eigen::AlignedBox &bb): box{bb} {}
+
+ bool operator() (const typename Tree::Node &node) const
+ {
+ return box.contains(node.bbox);
+ }
+};
+
+template auto containing(const G &g) { return Containing{g}; }
+
+namespace detail {
+
+template
+void traverse_recurse(const Tree &tree,
+ size_t idx,
+ Pred && pred,
+ Fn && callback)
+{
+ assert(tree.node(idx).is_valid());
+
+ if (!pred(tree.node(idx))) return;
+
+ if (tree.node(idx).is_leaf()) {
+ callback(tree.node(idx).idx);
+ } else {
+
+ // call this with left and right node idx:
+ auto trv = [&](size_t idx) {
+ traverse_recurse(tree, idx, std::forward(pred),
+ std::forward(callback));
+ };
+
+ // Left / right child node index.
+ trv(Tree::left_child_idx(idx));
+ trv(Tree::right_child_idx(idx));
+ }
+}
+
+} // namespace detail
+
+// Tree traversal with a predicate. Example usage:
+// traverse(tree, intersecting(QueryBox), [](size_t face_idx) {
+// /* ... */
+// });
+template
+void traverse(const Tree &tree, Predicate &&pred, Fn &&callback)
+{
+ if (tree.empty()) return;
+
+ detail::traverse_recurse(tree, size_t(0), std::forward(pred),
+ std::forward(callback));
+}
} // namespace AABBTreeIndirect
} // namespace Slic3r
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index 08e5fdf6d8..d9301d1f3d 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -68,14 +68,12 @@ void AppConfig::set_defaults()
if (get("export_sources_full_pathnames").empty())
set("export_sources_full_pathnames", "0");
-#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
#ifdef _WIN32
if (get("associate_3mf").empty())
set("associate_3mf", "0");
if (get("associate_stl").empty())
set("associate_stl", "0");
#endif // _WIN32
-#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
// remove old 'use_legacy_opengl' parameter from this config, if present
if (!get("use_legacy_opengl").empty())
@@ -127,14 +125,12 @@ void AppConfig::set_defaults()
if (get("color_mapinulation_panel").empty())
set("color_mapinulation_panel", "0");
}
-#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
else {
#ifdef _WIN32
if (get("associate_gcode").empty())
set("associate_gcode", "0");
#endif // _WIN32
}
-#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
if (get("seq_top_layer_only").empty())
set("seq_top_layer_only", "1");
diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp
index eb4e042a08..4f52c5108d 100644
--- a/src/libslic3r/BoundingBox.cpp
+++ b/src/libslic3r/BoundingBox.cpp
@@ -225,24 +225,11 @@ BoundingBox3Base::max_size() const
template coordf_t BoundingBox3Base::max_size() const;
template coordf_t BoundingBox3Base::max_size() const;
-// Align a coordinate to a grid. The coordinate may be negative,
-// the aligned value will never be bigger than the original one.
-static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
- // Current C++ standard defines the result of integer division to be rounded to zero,
- // for both positive and negative numbers. Here we want to round down for negative
- // numbers as well.
- coord_t aligned = (coord < 0) ?
- ((coord - spacing + 1) / spacing) * spacing :
- (coord / spacing) * spacing;
- assert(aligned <= coord);
- return aligned;
-}
-
void BoundingBox::align_to_grid(const coord_t cell_size)
{
if (this->defined) {
- min(0) = _align_to_grid(min(0), cell_size);
- min(1) = _align_to_grid(min(1), cell_size);
+ min(0) = Slic3r::align_to_grid(min(0), cell_size);
+ min(1) = Slic3r::align_to_grid(min(1), cell_size);
}
}
diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp
index 8de28af5cd..37483fc3e2 100644
--- a/src/libslic3r/BoundingBox.hpp
+++ b/src/libslic3r/BoundingBox.hpp
@@ -21,22 +21,30 @@ public:
min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {}
BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
min(p1), max(p1), defined(false) { merge(p2); merge(p3); }
- BoundingBoxBase(const std::vector& points) : min(PointClass::Zero()), max(PointClass::Zero())
+
+ template >
+ BoundingBoxBase(It from, It to) : min(PointClass::Zero()), max(PointClass::Zero())
{
- if (points.empty()) {
+ if (from == to) {
this->defined = false;
// throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBoxBase constructor");
} else {
- typename std::vector::const_iterator it = points.begin();
- this->min = *it;
- this->max = *it;
- for (++ it; it != points.end(); ++ it) {
- this->min = this->min.cwiseMin(*it);
- this->max = this->max.cwiseMax(*it);
+ auto it = from;
+ this->min = it->template cast();
+ this->max = this->min;
+ for (++ it; it != to; ++ it) {
+ auto vec = it->template cast();
+ this->min = this->min.cwiseMin(vec);
+ this->max = this->max.cwiseMax(vec);
}
this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
}
}
+
+ BoundingBoxBase(const std::vector &points)
+ : BoundingBoxBase(points.begin(), points.end())
+ {}
+
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); }
void merge(const PointClass &point);
void merge(const std::vector &points);
@@ -74,19 +82,27 @@ public:
{ if (pmin(2) >= pmax(2)) BoundingBoxBase::defined = false; }
BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
BoundingBoxBase(p1, p1) { merge(p2); merge(p3); }
- BoundingBox3Base(const std::vector& points)
+
+ template > BoundingBox3Base(It from, It to)
{
- if (points.empty())
+ if (from == to)
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
- typename std::vector::const_iterator it = points.begin();
- this->min = *it;
- this->max = *it;
- for (++ it; it != points.end(); ++ it) {
- this->min = this->min.cwiseMin(*it);
- this->max = this->max.cwiseMax(*it);
+
+ auto it = from;
+ this->min = it->template cast();
+ this->max = this->min;
+ for (++ it; it != to; ++ it) {
+ auto vec = it->template cast();
+ this->min = this->min.cwiseMin(vec);
+ this->max = this->max.cwiseMax(vec);
}
this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)) && (this->min(2) < this->max(2));
}
+
+ BoundingBox3Base(const std::vector &points)
+ : BoundingBox3Base(points.begin(), points.end())
+ {}
+
void merge(const PointClass &point);
void merge(const std::vector &points);
void merge(const BoundingBox3Base &bb);
@@ -188,9 +204,7 @@ public:
class BoundingBoxf3 : public BoundingBox3Base
{
public:
- BoundingBoxf3() : BoundingBox3Base() {}
- BoundingBoxf3(const Vec3d &pmin, const Vec3d &pmax) : BoundingBox3Base(pmin, pmax) {}
- BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {}
+ using BoundingBox3Base::BoundingBox3Base;
BoundingBoxf3 transformed(const Transform3d& matrix) const;
};
diff --git a/src/libslic3r/BridgeDetector.cpp b/src/libslic3r/BridgeDetector.cpp
index ff33e81d53..671ebbdaad 100644
--- a/src/libslic3r/BridgeDetector.cpp
+++ b/src/libslic3r/BridgeDetector.cpp
@@ -40,7 +40,7 @@ void BridgeDetector::initialize()
this->angle = -1.;
// Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
- Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing));
+ Polygons grown = offset(this->expolygons, float(this->spacing));
// Detect possible anchoring edges of this bridging region.
// Detect what edges lie on lower slices by turning bridge contour and holes
diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp
index d5ec0d9285..08bedc5c04 100644
--- a/src/libslic3r/Brim.cpp
+++ b/src/libslic3r/Brim.cpp
@@ -320,7 +320,7 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_
loops = union_pt_chained_outside_in(loops, false);
std::reverse(loops.begin(), loops.end());
extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()),
- float(flow.width), float(print.skirt_first_layer_height()));
+ float(flow.width()), float(print.skirt_first_layer_height()));
}
// Produce brim lines around those objects, that have the brim enabled.
@@ -495,7 +495,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) {
auto *loop = new ExtrusionLoop();
brim.entities.emplace_back(loop);
- loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height()));
+ loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
Points &points = loop->paths.front().polyline.points;
points.reserve(first_path.size());
for (const ClipperLib_Z::IntPoint &pt : first_path)
@@ -506,7 +506,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
ExtrusionEntityCollection this_loop_trimmed;
this_loop_trimmed.entities.reserve(j - i);
for (; i < j; ++ i) {
- this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height())));
+ this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())));
const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
Points &points = static_cast(this_loop_trimmed.entities.back())->polyline.points;
points.reserve(path.size());
@@ -522,7 +522,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
}
}
} else {
- extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height()));
+ extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
}
make_inner_brim(print, top_level_objects_with_brim, brim);
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index b12ae84193..507d2f0aa7 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -211,10 +211,14 @@ add_library(libslic3r STATIC
PerimeterGenerator.hpp
PlaceholderParser.cpp
PlaceholderParser.hpp
+ Platform.cpp
+ Platform.hpp
Point.cpp
Point.hpp
Polygon.cpp
Polygon.hpp
+ MutablePolygon.cpp
+ MutablePolygon.hpp
PolygonTrimmer.cpp
PolygonTrimmer.hpp
Polyline.cpp
@@ -285,6 +289,9 @@ add_library(libslic3r STATIC
SimplifyMeshImpl.hpp
SimplifyMesh.cpp
MarchingSquares.hpp
+ Execution/Execution.hpp
+ Execution/ExecutionSeq.hpp
+ Execution/ExecutionTBB.hpp
Optimize/Optimizer.hpp
Optimize/NLoptOptimizer.hpp
Optimize/BruteforceOptimizer.hpp
diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp
index 305ea134f0..cd243dfb1b 100644
--- a/src/libslic3r/ClipperUtils.cpp
+++ b/src/libslic3r/ClipperUtils.cpp
@@ -57,6 +57,7 @@ err:
}
#endif /* CLIPPER_UTILS_DEBUG */
+#ifdef CLIPPERUTILS_OFFSET_SCALE
void scaleClipperPolygon(ClipperLib::Path &polygon)
{
CLIPPERUTILS_PROFILE_FUNC();
@@ -98,6 +99,7 @@ void unscaleClipperPolygons(ClipperLib::Paths &polygons)
pit->Y >>= CLIPPER_OFFSET_POWER_OF_2;
}
}
+#endif // CLIPPERUTILS_OFFSET_SCALE
//-----------------------------------------------------------
// legacy code from Clipper documentation
@@ -222,8 +224,10 @@ ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input)
ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit)
{
+#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input
scaleClipperPolygons(input);
+#endif // CLIPPERUTILS_OFFSET_SCALE
// perform offset
ClipperLib::ClipperOffset co;
@@ -231,14 +235,20 @@ ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType
co.ArcTolerance = miterLimit;
else
co.MiterLimit = miterLimit;
+#ifdef CLIPPERUTILS_OFFSET_SCALE
float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
+#else // CLIPPERUTILS_OFFSET_SCALE
+ float delta_scaled = delta;
+#endif // CLIPPERUTILS_OFFSET_SCALE
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
co.AddPaths(input, joinType, endType);
ClipperLib::Paths retval;
co.Execute(retval, delta_scaled);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output
unscaleClipperPolygons(retval);
+#endif // CLIPPERUTILS_OFFSET_SCALE
return retval;
}
@@ -257,14 +267,24 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
{
// printf("new ExPolygon offset\n");
// 1) Offset the outer contour.
- const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
+ float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
+#else // CLIPPERUTILS_OFFSET_SCALE
+ float delta_scaled = delta;
+#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::Paths contours;
{
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(expolygon.contour);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input);
+#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
+#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
+#else // CLIPPERUTILS_OFFSET_SCALE
+ co.ArcTolerance = miterLimit;
+#endif // CLIPPERUTILS_OFFSET_SCALE
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@@ -278,17 +298,23 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
holes.reserve(expolygon.holes.size());
for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) {
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input);
+#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
+#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
+#else // CLIPPERUTILS_OFFSET_SCALE
+ co.ArcTolerance = miterLimit;
+#endif // CLIPPERUTILS_OFFSET_SCALE
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
co.AddPath(input, joinType, ClipperLib::etClosedPolygon);
ClipperLib::Paths out;
co.Execute(out, - delta_scaled);
- holes.insert(holes.end(), out.begin(), out.end());
+ append(holes, std::move(out));
}
}
@@ -305,7 +331,9 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
}
// 4) Unscale the output.
+#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(output);
+#endif // CLIPPERUTILS_OFFSET_SCALE
return output;
}
@@ -315,7 +343,11 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta,
ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta,
ClipperLib::JoinType joinType, double miterLimit)
{
- const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
+ float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE);
+#else // CLIPPERUTILS_OFFSET_SCALE
+ float delta_scaled = delta;
+#endif // CLIPPERUTILS_OFFSET_SCALE
// Offsetted ExPolygons before they are united.
ClipperLib::Paths contours_cummulative;
contours_cummulative.reserve(expolygons.size());
@@ -327,10 +359,16 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
ClipperLib::Paths contours;
{
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input);
+#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
+#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
+#else // CLIPPERUTILS_OFFSET_SCALE
+ co.ArcTolerance = miterLimit;
+#endif // CLIPPERUTILS_OFFSET_SCALE
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@@ -351,10 +389,16 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
{
for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) {
ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
scaleClipperPolygon(input);
+#endif // CLIPPERUTILS_OFFSET_SCALE
ClipperLib::ClipperOffset co;
if (joinType == jtRound)
+#ifdef CLIPPERUTILS_OFFSET_SCALE
co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE);
+#else // CLIPPERUTILS_OFFSET_SCALE
+ co.ArcTolerance = miterLimit;
+#endif // CLIPPERUTILS_OFFSET_SCALE
else
co.MiterLimit = miterLimit;
co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR));
@@ -413,8 +457,10 @@ ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delt
output = std::move(contours_cummulative);
}
+#ifdef CLIPPERUTILS_OFFSET_SCALE
// 4) Unscale the output.
unscaleClipperPolygons(output);
+#endif // CLIPPERUTILS_OFFSET_SCALE
return output;
}
@@ -425,8 +471,10 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
// read input
ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input
scaleClipperPolygons(input);
+#endif // CLIPPERUTILS_OFFSET_SCALE
// prepare ClipperOffset object
ClipperLib::ClipperOffset co;
@@ -435,8 +483,13 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
} else {
co.MiterLimit = miterLimit;
}
+#ifdef CLIPPERUTILS_OFFSET_SCALE
float delta_scaled1 = delta1 * float(CLIPPER_OFFSET_SCALE);
float delta_scaled2 = delta2 * float(CLIPPER_OFFSET_SCALE);
+#else // CLIPPERUTILS_OFFSET_SCALE
+ float delta_scaled1 = delta1;
+ float delta_scaled2 = delta2;
+#endif // CLIPPERUTILS_OFFSET_SCALE
co.ShortestEdgeLength = double(std::max(std::abs(delta_scaled1), std::abs(delta_scaled2)) * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR);
// perform first offset
@@ -450,8 +503,10 @@ _offset2(const Polygons &polygons, const float delta1, const float delta2,
ClipperLib::Paths retval;
co.Execute(retval, delta_scaled2);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output
unscaleClipperPolygons(retval);
+#endif // CLIPPERUTILS_OFFSET_SCALE
return retval;
}
@@ -789,8 +844,10 @@ void safety_offset(ClipperLib::Paths* paths)
{
CLIPPERUTILS_PROFILE_FUNC();
+#ifdef CLIPPERUTILS_OFFSET_SCALE
// scale input
scaleClipperPolygons(*paths);
+#endif // CLIPPERUTILS_OFFSET_SCALE
// perform offset (delta = scale 1e-05)
ClipperLib::ClipperOffset co;
@@ -816,7 +873,11 @@ void safety_offset(ClipperLib::Paths* paths)
CLIPPERUTILS_PROFILE_BLOCK(safety_offset_Execute);
// offset outside by 10um
ClipperLib::Paths out_this;
+#ifdef CLIPPERUTILS_OFFSET_SCALE
co.Execute(out_this, ccw ? 10.f * float(CLIPPER_OFFSET_SCALE) : -10.f * float(CLIPPER_OFFSET_SCALE));
+#else // CLIPPERUTILS_OFFSET_SCALE
+ co.Execute(out_this, ccw ? 10.f : -10.f);
+#endif // CLIPPERUTILS_OFFSET_SCALE
if (! ccw) {
// Reverse the resulting contours once again.
for (ClipperLib::Paths::iterator it = out_this.begin(); it != out_this.end(); ++ it)
@@ -830,8 +891,10 @@ void safety_offset(ClipperLib::Paths* paths)
}
*paths = std::move(out);
+#ifdef CLIPPERUTILS_OFFSET_SCALE
// unscale output
unscaleClipperPolygons(*paths);
+#endif // CLIPPERUTILS_OFFSET_SCALE
}
Polygons top_level_islands(const Slic3r::Polygons &polygons)
@@ -925,8 +988,10 @@ ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::v
// Add a new point to the output, scale by CLIPPER_OFFSET_SCALE and round to ClipperLib::cInt.
auto add_offset_point = [&out](Vec2d pt) {
+#ifdef CLIPPERUTILS_OFFSET_SCALE
pt *= double(CLIPPER_OFFSET_SCALE);
- pt += Vec2d(0.5 - (pt.x() < 0), 0.5 - (pt.y() < 0));
+#endif // CLIPPERUTILS_OFFSET_SCALE
+ pt += Vec2d(0.5 - (pt.x() < 0), 0.5 - (pt.y() < 0));
out.emplace_back(ClipperLib::cInt(pt.x()), ClipperLib::cInt(pt.y()));
};
@@ -1075,8 +1140,10 @@ Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector& ds : deltas)
clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
}
+#ifdef CLIPPERUTILS_OFFSET_SCALE
// 4) Unscale the output.
unscaleClipperPolygons(output);
+#endif // CLIPPERUTILS_OFFSET_SCALE
return ClipperPaths_to_Slic3rPolygons(output);
}
@@ -1152,7 +1221,9 @@ for (const std::vector& ds : deltas)
#endif /* NDEBUG */
// 3) Subtract holes from the contours.
+#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(contours);
+#endif // CLIPPERUTILS_OFFSET_SCALE
ExPolygons output;
if (holes.empty()) {
output.reserve(contours.size());
@@ -1160,7 +1231,9 @@ for (const std::vector& ds : deltas)
output.emplace_back(ClipperPath_to_Slic3rPolygon(path));
} else {
ClipperLib::Clipper clipper;
+#ifdef CLIPPERUTILS_OFFSET_SCALE
unscaleClipperPolygons(holes);
+#endif // CLIPPERUTILS_OFFSET_SCALE
clipper.AddPaths(contours, ClipperLib::ptSubject, true);
clipper.AddPaths(holes, ClipperLib::ptClip, true);
ClipperLib::PolyTree polytree;
@@ -1200,7 +1273,9 @@ ExPolygons variable_offset_inner_ex(const ExPolygon &expoly, const std::vector &deltas, double miter_limit);
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit = 2.);
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit = 2.);
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector> &deltas, double miter_limit = 2.);
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index 9c9b5d3486..9aab435edd 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -1441,6 +1441,24 @@ private:
class ConfigOptionDef
{
public:
+ enum class GUIType {
+ undefined,
+ // Open enums, integer value could be one of the enumerated values or something else.
+ i_enum_open,
+ // Open enums, float value could be one of the enumerated values or something else.
+ f_enum_open,
+ // Color picker, string value.
+ color,
+ // ???
+ select_open,
+ // Currently unused.
+ slider,
+ // Static text
+ legend,
+ // Vector value, but edited as a single string.
+ one_string,
+ };
+
// Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map.
t_config_option_key opt_key;
// What type? bool, int, string etc.
@@ -1524,7 +1542,7 @@ public:
// Usually empty.
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
// "select_open" - to open a selection dialog (currently only a serial port selection).
- std::string gui_type;
+ GUIType gui_type { GUIType::undefined };
// Usually empty. Otherwise "serialized" or "show_value"
// The flags may be combined.
// "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon.
@@ -1949,8 +1967,9 @@ public:
int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); }
// In ConfigManipulation::toggle_print_fff_options, it is called on option with type ConfigOptionEnumGeneric* and also ConfigOptionEnum*.
+ // Thus the virtual method getInt() is used to retrieve the enum value.
template
- ENUM opt_enum(const t_config_option_key &opt_key) const { return this->option>(opt_key)->value; }
+ ENUM opt_enum(const t_config_option_key &opt_key) const { return static_cast(this->option(opt_key)->getInt()); }
bool opt_bool(const t_config_option_key &opt_key) const { return this->option(opt_key)->value != 0; }
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option(opt_key)->get_at(idx) != 0; }
diff --git a/src/libslic3r/ElephantFootCompensation.cpp b/src/libslic3r/ElephantFootCompensation.cpp
index f28d88f7e9..0895e16d68 100644
--- a/src/libslic3r/ElephantFootCompensation.cpp
+++ b/src/libslic3r/ElephantFootCompensation.cpp
@@ -621,7 +621,7 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
ExPolygon elephant_foot_compensation(const ExPolygon &input, const Flow &external_perimeter_flow, const double compensation)
{
// The contour shall be wide enough to apply the external perimeter plus compensation on both sides.
- double min_contour_width = double(external_perimeter_flow.width + external_perimeter_flow.spacing());
+ double min_contour_width = double(external_perimeter_flow.width() + external_perimeter_flow.spacing());
return elephant_foot_compensation(input, min_contour_width, compensation);
}
diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp
index 46b3a3a1b9..73770bb185 100644
--- a/src/libslic3r/ExPolygon.hpp
+++ b/src/libslic3r/ExPolygon.hpp
@@ -217,6 +217,28 @@ inline Polygons to_polygons(const ExPolygons &src)
return polygons;
}
+inline ConstPolygonPtrs to_polygon_ptrs(const ExPolygon &src)
+{
+ ConstPolygonPtrs polygons;
+ polygons.reserve(src.holes.size() + 1);
+ polygons.emplace_back(&src.contour);
+ for (const Polygon &hole : src.holes)
+ polygons.emplace_back(&hole);
+ return polygons;
+}
+
+inline ConstPolygonPtrs to_polygon_ptrs(const ExPolygons &src)
+{
+ ConstPolygonPtrs polygons;
+ polygons.reserve(number_polygons(src));
+ for (const ExPolygon &expoly : src) {
+ polygons.emplace_back(&expoly.contour);
+ for (const Polygon &hole : expoly.holes)
+ polygons.emplace_back(&hole);
+ }
+ return polygons;
+}
+
inline Polygons to_polygons(ExPolygon &&src)
{
Polygons polygons;
diff --git a/src/libslic3r/Execution/Execution.hpp b/src/libslic3r/Execution/Execution.hpp
new file mode 100644
index 0000000000..e4bad9f237
--- /dev/null
+++ b/src/libslic3r/Execution/Execution.hpp
@@ -0,0 +1,132 @@
+#ifndef EXECUTION_HPP
+#define EXECUTION_HPP
+
+#include
+#include
+#include
+#include
+
+#include "libslic3r/libslic3r.h"
+
+namespace Slic3r {
+
+// Borrowed from C++20
+template
+using remove_cvref_t = std::remove_reference_t>;
+
+// Override for valid execution policies
+template struct IsExecutionPolicy_ : public std::false_type {};
+
+template constexpr bool IsExecutionPolicy =
+ IsExecutionPolicy_>::value;
+
+template
+using ExecutionPolicyOnly = std::enable_if_t, T>;
+
+namespace execution {
+
+// This struct needs to be specialized for each execution policy.
+// See ExecutionSeq.hpp and ExecutionTBB.hpp for example.
+template struct Traits {};
+
+template using AsTraits = Traits>;
+
+// Each execution policy should declare two types of mutexes. A a spin lock and
+// a blocking mutex. These types should satisfy the BasicLockable concept.
+template using SpinningMutex = typename Traits::SpinningMutex;
+template using BlockingMutex = typename Traits::BlockingMutex;
+
+// Query the available threads for concurrency.
+template >
+size_t max_concurrency(const EP &ep)
+{
+ return AsTraits::max_concurrency(ep);
+}
+
+// foreach loop with the execution policy passed as argument. Granularity can
+// be specified explicitly. max_concurrency() can be used for optimal results.
+template>
+void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity = 1)
+{
+ AsTraits::for_each(ep, from, to, std::forward(fn), granularity);
+}
+
+// A reduce operation with the execution policy passed as argument.
+// mergefn has T(const T&, const T&) signature
+// accessfn has T(I) signature if I is an integral type and
+// T(const I::value_type &) if I is an iterator type.
+template >
+T reduce(const EP & ep,
+ I from,
+ I to,
+ const T & init,
+ MergeFn && mergefn,
+ AccessFn &&accessfn,
+ size_t granularity = 1)
+{
+ return AsTraits::reduce(ep, from, to, init,
+ std::forward(mergefn),
+ std::forward(accessfn),
+ granularity);
+}
+
+// An overload of reduce method to be used with iterators as 'from' and 'to'
+// arguments. Access functor is omitted here.
+template >
+T reduce(const EP &ep,
+ I from,
+ I to,
+ const T & init,
+ MergeFn &&mergefn,
+ size_t granularity = 1)
+{
+ return reduce(
+ ep, from, to, init, std::forward(mergefn),
+ [](const auto &i) { return i; }, granularity);
+}
+
+template>
+T accumulate(const EP & ep,
+ I from,
+ I to,
+ const T & init,
+ AccessFn &&accessfn,
+ size_t granularity = 1)
+{
+ return reduce(ep, from, to, init, std::plus{},
+ std::forward(accessfn), granularity);
+}
+
+
+template >
+T accumulate(const EP &ep,
+ I from,
+ I to,
+ const T & init,
+ size_t granularity = 1)
+{
+ return reduce(
+ ep, from, to, init, std::plus{}, [](const auto &i) { return i; },
+ granularity);
+}
+
+} // namespace execution_policy
+} // namespace Slic3r
+
+#endif // EXECUTION_HPP
diff --git a/src/libslic3r/Execution/ExecutionSeq.hpp b/src/libslic3r/Execution/ExecutionSeq.hpp
new file mode 100644
index 0000000000..321d65631b
--- /dev/null
+++ b/src/libslic3r/Execution/ExecutionSeq.hpp
@@ -0,0 +1,84 @@
+#ifndef EXECUTIONSEQ_HPP
+#define EXECUTIONSEQ_HPP
+
+#ifdef PRUSASLICER_USE_EXECUTION_STD // Conflicts with our version of TBB
+#include
+#endif
+
+#include "Execution.hpp"
+
+namespace Slic3r {
+
+// Execution policy implementing dummy sequential algorithms
+struct ExecutionSeq {};
+
+template<> struct IsExecutionPolicy_ : public std::true_type {};
+
+static constexpr ExecutionSeq ex_seq = {};
+
+template struct IsSequentialEP_ { static constexpr bool value = false; };
+
+template<> struct IsSequentialEP_: public std::true_type {};
+#ifdef PRUSASLICER_USE_EXECUTION_STD
+template<> struct IsExecutionPolicy_: public std::true_type {};
+template<> struct IsSequentialEP_: public std::true_type {};
+#endif
+
+template
+constexpr bool IsSequentialEP = IsSequentialEP_>::value;
+
+template
+using SequentialEPOnly = std::enable_if_t, R>;
+
+template
+struct execution::Traits> {
+private:
+ struct _Mtx { inline void lock() {} inline void unlock() {} };
+
+ template
+ static IteratorOnly loop_(It from, It to, Fn &&fn)
+ {
+ for (auto it = from; it != to; ++it) fn(*it);
+ }
+
+ template
+ static IntegerOnly loop_(I from, I to, Fn &&fn)
+ {
+ for (I i = from; i < to; ++i) fn(i);
+ }
+
+public:
+ using SpinningMutex = _Mtx;
+ using BlockingMutex = _Mtx;
+
+ template
+ static void for_each(const EP &,
+ It from,
+ It to,
+ Fn &&fn,
+ size_t /* ignore granularity */ = 1)
+ {
+ loop_(from, to, std::forward(fn));
+ }
+
+ template
+ static T reduce(const EP &,
+ I from,
+ I to,
+ const T & init,
+ MergeFn &&mergefn,
+ AccessFn &&access,
+ size_t /*granularity*/ = 1
+ )
+ {
+ T acc = init;
+ loop_(from, to, [&](auto &i) { acc = mergefn(acc, access(i)); });
+ return acc;
+ }
+
+ static size_t max_concurrency(const EP &) { return 1; }
+};
+
+} // namespace Slic3r
+
+#endif // EXECUTIONSEQ_HPP
diff --git a/src/libslic3r/Execution/ExecutionTBB.hpp b/src/libslic3r/Execution/ExecutionTBB.hpp
new file mode 100644
index 0000000000..cf6373c466
--- /dev/null
+++ b/src/libslic3r/Execution/ExecutionTBB.hpp
@@ -0,0 +1,77 @@
+#ifndef EXECUTIONTBB_HPP
+#define EXECUTIONTBB_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+#include "Execution.hpp"
+
+namespace Slic3r {
+
+struct ExecutionTBB {};
+template<> struct IsExecutionPolicy_ : public std::true_type {};
+
+// Execution policy using Intel TBB library under the hood.
+static constexpr ExecutionTBB ex_tbb = {};
+
+template<> struct execution::Traits {
+private:
+
+ template
+ static IteratorOnly loop_(const tbb::blocked_range &range, Fn &&fn)
+ {
+ for (auto &el : range) fn(el);
+ }
+
+ template
+ static IntegerOnly loop_(const tbb::blocked_range &range, Fn &&fn)
+ {
+ for (I i = range.begin(); i < range.end(); ++i) fn(i);
+ }
+
+public:
+ using SpinningMutex = tbb::spin_mutex;
+ using BlockingMutex = tbb::mutex;
+
+ template
+ static void for_each(const ExecutionTBB &,
+ It from, It to, Fn &&fn, size_t granularity)
+ {
+ tbb::parallel_for(tbb::blocked_range{from, to, granularity},
+ [&fn](const auto &range) {
+ loop_(range, std::forward(fn));
+ });
+ }
+
+ template
+ static T reduce(const ExecutionTBB &,
+ I from,
+ I to,
+ const T &init,
+ MergeFn &&mergefn,
+ AccessFn &&access,
+ size_t granularity = 1
+ )
+ {
+ return tbb::parallel_reduce(
+ tbb::blocked_range{from, to, granularity}, init,
+ [&](const auto &range, T subinit) {
+ T acc = subinit;
+ loop_(range, [&](auto &i) { acc = mergefn(acc, access(i)); });
+ return acc;
+ },
+ std::forward(mergefn));
+ }
+
+ static size_t max_concurrency(const ExecutionTBB &)
+ {
+ return tbb::this_task_arena::max_concurrency();
+ }
+};
+
+}
+
+#endif // EXECUTIONTBB_HPP
diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp
index 390d107f27..3284bc39e4 100644
--- a/src/libslic3r/ExtrusionEntity.cpp
+++ b/src/libslic3r/ExtrusionEntity.cpp
@@ -52,7 +52,9 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale
{
// Instantiating the Flow class to get the line spacing.
// Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler.
- Flow flow(this->width, this->height, 0.f, is_bridge(this->role()));
+ bool bridge = is_bridge(this->role());
+ assert(! bridge || this->width == this->height);
+ auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f);
polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
}
diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp
index 2c43cd4af8..2f35083169 100644
--- a/src/libslic3r/ExtrusionEntity.hpp
+++ b/src/libslic3r/ExtrusionEntity.hpp
@@ -203,6 +203,8 @@ public:
void reverse() override;
const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
const Point& last_point() const override { return this->paths.back().polyline.points.back(); }
+ size_t size() const { return this->paths.size(); }
+ bool empty() const { return this->paths.empty(); }
double length() const override;
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index ee493ca9cb..579259a5fc 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -28,6 +28,8 @@ struct SurfaceFillParams
// coordf_t overlap = 0.;
// Angle as provided by the region config, in radians.
float angle = 0.f;
+ // Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set.
+ bool bridge;
// Non-negative for a bridge.
float bridge_angle = 0.f;
@@ -42,7 +44,7 @@ struct SurfaceFillParams
// width, height of extrusion, nozzle diameter, is bridge
// For the output, for fill generator.
- Flow flow = Flow(0.f, 0.f, 0.f, false);
+ Flow flow;
// For the output
ExtrusionRole extrusion_role = ExtrusionRole(0);
@@ -70,21 +72,22 @@ struct SurfaceFillParams
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
RETURN_COMPARE_NON_EQUAL(anchor_length);
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
- RETURN_COMPARE_NON_EQUAL(flow.width);
- RETURN_COMPARE_NON_EQUAL(flow.height);
- RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
- RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge);
+ RETURN_COMPARE_NON_EQUAL(flow.width());
+ RETURN_COMPARE_NON_EQUAL(flow.height());
+ RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter());
+ RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, bridge);
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role);
return false;
}
bool operator==(const SurfaceFillParams &rhs) const {
return this->extruder == rhs.extruder &&
- this->pattern == rhs.pattern &&
this->pattern == rhs.pattern &&
this->spacing == rhs.spacing &&
// this->overlap == rhs.overlap &&
this->angle == rhs.angle &&
+ this->bridge == rhs.bridge &&
+// this->bridge_angle == rhs.bridge_angle &&
this->density == rhs.density &&
// this->dont_adjust == rhs.dont_adjust &&
this->anchor_length == rhs.anchor_length &&
@@ -128,6 +131,7 @@ std::vector group_fills(const Layer &layer)
if (surface.is_solid()) {
params.density = 100.f;
+ //FIXME for non-thick bridges, shall we allow a bottom surface pattern?
params.pattern = (surface.is_external() && ! is_bridge) ?
(surface.is_top() ? region_config.top_fill_pattern.value : region_config.bottom_fill_pattern.value) :
region_config.top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear;
@@ -143,17 +147,13 @@ std::vector group_fills(const Layer &layer)
params.bridge_angle = float(surface.bridge_angle);
params.angle = float(Geometry::deg2rad(region_config.fill_angle.value));
- // calculate the actual flow we'll be using for this infill
- params.flow = layerm.region()->flow(
- extrusion_role,
- (surface.thickness == -1) ? layer.height : surface.thickness, // extrusion height
- is_bridge || Fill::use_bridge_flow(params.pattern), // bridge flow?
- layer.id() == 0, // first layer?
- -1, // auto width
- *layer.object()
- );
-
- // Calculate flow spacing for infill pattern generation.
+ // Calculate the actual flow we'll be using for this infill.
+ params.bridge = is_bridge || Fill::use_bridge_flow(params.pattern);
+ params.flow = params.bridge ?
+ layerm.bridging_flow(extrusion_role) :
+ layerm.flow(extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness);
+
+ // Calculate flow spacing for infill pattern generation.
if (surface.is_solid() || is_bridge) {
params.spacing = params.flow.spacing();
// Don't limit anchor length for solid or bridging infill.
@@ -164,14 +164,7 @@ std::vector group_fills(const Layer &layer)
// for all layers, for avoiding the ugly effect of
// misaligned infill on first layer because of different extrusion width and
// layer height
- params.spacing = layerm.region()->flow(
- frInfill,
- layer.object()->config().layer_height.value, // TODO: handle infill_every_layers?
- false, // no bridge
- false, // no first layer
- -1, // auto width
- *layer.object()
- ).spacing();
+ params.spacing = layerm.flow(frInfill, layer.object()->config().layer_height).spacing();
// Anchor a sparse infill to inner perimeters with the following anchor length:
params.anchor_length = float(region_config.infill_anchor);
if (region_config.infill_anchor.percent)
@@ -278,7 +271,7 @@ std::vector group_fills(const Layer &layer)
region_id = region_some_infill;
const LayerRegion& layerm = *layer.regions()[region_id];
for (SurfaceFill &surface_fill : surface_fills)
- if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height) < EPSILON) {
+ if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height()) < EPSILON) {
internal_solid_fill = &surface_fill;
break;
}
@@ -290,14 +283,7 @@ std::vector group_fills(const Layer &layer)
params.extrusion_role = erInternalInfill;
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
// calculate the actual flow we'll be using for this infill
- params.flow = layerm.region()->flow(
- frSolidInfill,
- layer.height, // extrusion height
- false, // bridge flow?
- layer.id() == 0, // first layer?
- -1, // auto width
- *layer.object()
- );
+ params.flow = layerm.flow(frSolidInfill);
params.spacing = params.flow.spacing();
surface_fills.emplace_back(params);
surface_fills.back().surface.surface_type = stInternalSolid;
@@ -365,9 +351,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
// calculate flow spacing for infill pattern generation
- bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge;
+ bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
double link_max_length = 0.;
- if (! surface_fill.params.flow.bridge) {
+ if (! surface_fill.params.bridge) {
#if 0
link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
// printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
@@ -380,7 +366,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
// Maximum length of the perimeter segment linking two infill lines.
f->link_max_length = (coord_t)scale_(link_max_length);
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
- f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
+ f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter()) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
// apply half spacing using this flow's own spacing and generate infill
FillParams params;
@@ -402,15 +388,15 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
// calculate actual flow from spacing (which might have been adjusted by the infill
// pattern generator)
double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm();
- double flow_width = surface_fill.params.flow.width;
+ double flow_width = surface_fill.params.flow.width();
if (using_internal_flow) {
// if we used the internal flow we're not doing a solid infill
// so we can safely ignore the slight variation that might have
// been applied to f->spacing
} else {
- Flow new_flow = Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter, surface_fill.params.flow.height, surface_fill.params.flow.bridge);
+ Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing));
flow_mm3_per_mm = new_flow.mm3_per_mm();
- flow_width = new_flow.width;
+ flow_width = new_flow.width();
}
// Save into layer.
ExtrusionEntityCollection* eec = nullptr;
@@ -420,7 +406,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
extrusion_entities_append_paths(
eec->entities, std::move(polylines),
surface_fill.params.extrusion_role,
- flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height);
+ flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height());
}
}
}
@@ -618,9 +604,9 @@ void Layer::make_ironing()
fill.spacing = ironing_params.line_spacing;
fill.angle = float(ironing_params.angle + 0.25 * M_PI);
fill.link_max_length = (coord_t)scale_(3. * fill.spacing);
- double height = ironing_params.height * fill.spacing / nozzle_dmr;
- Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height), false);
- double flow_mm3_per_mm = flow.mm3_per_mm();
+ double extrusion_height = ironing_params.height * fill.spacing / nozzle_dmr;
+ float extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(float(nozzle_dmr), float(extrusion_height));
+ double flow_mm3_per_mm = nozzle_dmr * extrusion_height;
Surface surface_fill(stTop, ExPolygon());
for (ExPolygon &expoly : ironing_areas) {
surface_fill.expolygon = std::move(expoly);
@@ -638,7 +624,7 @@ void Layer::make_ironing()
extrusion_entities_append_paths(
eec->entities, std::move(polylines),
erIroning,
- flow_mm3_per_mm, float(flow.width), float(height));
+ flow_mm3_per_mm, extrusion_width, float(extrusion_height));
}
}
}
diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp
index 2ddca7fe4a..95c26fbad4 100644
--- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp
+++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp
@@ -147,7 +147,7 @@ void Fill3DHoneycomb::_fill_surface_single(
// align bounding box to a multiple of our honeycomb grid module
// (a module is 2*$distance since one $distance half-module is
// growing while the other $distance half-module is shrinking)
- bb.merge(_align_to_grid(bb.min, Point(2*distance, 2*distance)));
+ bb.merge(align_to_grid(bb.min, Point(2*distance, 2*distance)));
// generate pattern
Polylines polylines = makeGrid(
diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp
index b4afd25917..6d1d94ff8c 100644
--- a/src/libslic3r/Fill/FillBase.cpp
+++ b/src/libslic3r/Fill/FillBase.cpp
@@ -19,6 +19,8 @@
#include "FillRectilinear.hpp"
#include "FillAdaptive.hpp"
+// #define INFILL_DEBUG_OUTPUT
+
namespace Slic3r {
Fill* Fill::new_from_type(const InfillPattern type)
@@ -41,6 +43,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipOctagramSpiral: return new FillOctagramSpiral();
case ipAdaptiveCubic: return new FillAdaptive::Filler();
case ipSupportCubic: return new FillAdaptive::Filler();
+ case ipSupportBase: return new FillSupportBase();
default: throw Slic3r::InvalidArgument("unknown type");
}
}
@@ -253,15 +256,15 @@ std::pair path_lengths_along_contour(const ContourIntersectionPo
}
// Add contour points from interval (idx_start, idx_end> to polyline.
-static inline void take_cw_full(Polyline &pl, const Points& contour, size_t idx_start, size_t idx_end)
+static inline void take_cw_full(Polyline &pl, const Points &contour, size_t idx_start, size_t idx_end)
{
assert(! pl.empty() && pl.points.back() == contour[idx_start]);
- size_t i = (idx_end == 0) ? contour.size() - 1 : idx_start - 1;
+ size_t i = (idx_start == 0) ? contour.size() - 1 : idx_start - 1;
while (i != idx_end) {
pl.points.emplace_back(contour[i]);
if (i == 0)
i = contour.size();
- --i;
+ -- i;
}
pl.points.emplace_back(contour[i]);
}
@@ -612,13 +615,13 @@ static inline bool line_rounded_thick_segment_collision(
};
// Intersections with the inflated segment end points.
- auto ray_circle_intersection_interval_extend = [&extend_interval, &line_v0](const Vec2d &segment_pt, const double offset2, const Vec2d &line_pt, const Vec2d &line_vec) {
+ auto ray_circle_intersection_interval_extend = [&extend_interval](const Vec2d &segment_pt, const double offset2, const Vec2d &line_pt, const Vec2d &line_vec) {
std::pair pts;
Vec2d p0 = line_pt - segment_pt;
- double c = - line_pt.dot(p0);
- if (Geometry::ray_circle_intersections_r2_lv2_c(offset2, line_vec.x(), line_vec.y(), line_vec.squaredNorm(), c, pts)) {
- double tmin = (pts.first - p0).dot(line_v0);
- double tmax = (pts.second - p0).dot(line_v0);
+ double lv2 = line_vec.squaredNorm();
+ if (Geometry::ray_circle_intersections_r2_lv2_c(offset2, line_vec.y(), - line_vec.x(), lv2, - line_vec.y() * p0.x() + line_vec.x() * p0.y(), pts)) {
+ double tmin = (pts.first - p0).dot(line_vec) / lv2;
+ double tmax = (pts.second - p0).dot(line_vec) / lv2;
if (tmin > tmax)
std::swap(tmin, tmax);
tmin = std::max(tmin, 0.);
@@ -705,8 +708,6 @@ static inline bool cyclic_interval_inside_interval(double outer_low, double oute
}
#endif // NDEBUG
-// #define INFILL_DEBUG_OUTPUT
-
#ifdef INFILL_DEBUG_OUTPUT
static void export_infill_to_svg(
// Boundary contour, along which the perimeter extrusions will be drawn.
@@ -1099,61 +1100,216 @@ void Fill::connect_infill(Polylines &&infill_ordered, const Polygons &boundary_s
connect_infill(std::move(infill_ordered), polygons_src, bbox, polylines_out, spacing, params);
}
-void Fill::connect_infill(Polylines &&infill_ordered, const std::vector &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms)
+static constexpr auto boundary_idx_unconnected = std::numeric_limits::max();
+
+struct BoundaryInfillGraph
{
- assert(! infill_ordered.empty());
- assert(params.anchor_length >= 0.);
- assert(params.anchor_length_max >= 0.01f);
- assert(params.anchor_length_max >= params.anchor_length);
- const double anchor_length = scale_(params.anchor_length);
- const double anchor_length_max = scale_(params.anchor_length_max);
+ std::vector boundary;
+ std::vector> boundary_params;
+ std::vector map_infill_end_point_to_boundary;
-#if 0
- append(polylines_out, infill_ordered);
- return;
-#endif
+ const Point& point(const ContourIntersectionPoint &cp) const {
+ assert(cp.contour_idx != size_t(-1));
+ assert(cp.point_idx != size_t(-1));
+ return this->boundary[cp.contour_idx][cp.point_idx];
+ }
- // 1) Add the end points of infill_ordered to boundary_src.
- std::vector boundary;
- std::vector> boundary_params;
- boundary.assign(boundary_src.size(), Points());
- boundary_params.assign(boundary_src.size(), std::vector());
- // Mapping the infill_ordered end point to a (contour, point) of boundary.
- static constexpr auto boundary_idx_unconnected = std::numeric_limits::max();
- std::vector map_infill_end_point_to_boundary(infill_ordered.size() * 2, ContourIntersectionPoint{ boundary_idx_unconnected, boundary_idx_unconnected });
- {
- // Project the infill_ordered end points onto boundary_src.
- std::vector> intersection_points;
- {
- EdgeGrid::Grid grid;
- grid.set_bbox(bbox.inflated(SCALED_EPSILON));
- grid.create(boundary_src, coord_t(scale_(10.)));
- intersection_points.reserve(infill_ordered.size() * 2);
- for (const Polyline &pl : infill_ordered)
- for (const Point *pt : { &pl.points.front(), &pl.points.back() }) {
- EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point_signed_distance(*pt, coord_t(SCALED_EPSILON));
- if (cp.valid()) {
- // The infill end point shall lie on the contour.
- assert(cp.distance <= 3.);
- intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1));
- }
- }
- std::sort(intersection_points.begin(), intersection_points.end(), [](const std::pair &cp1, const std::pair &cp2) {
- return cp1.first.contour_idx < cp2.first.contour_idx ||
- (cp1.first.contour_idx == cp2.first.contour_idx &&
- (cp1.first.start_point_idx < cp2.first.start_point_idx ||
- (cp1.first.start_point_idx == cp2.first.start_point_idx && cp1.first.t < cp2.first.t)));
- });
- }
- auto it = intersection_points.begin();
- auto it_end = intersection_points.end();
- std::vector> boundary_intersection_points(boundary.size(), std::vector());
- for (size_t idx_contour = 0; idx_contour < boundary_src.size(); ++ idx_contour) {
+ const Point& infill_end_point(size_t infill_end_point_idx) const {
+ return this->point(this->map_infill_end_point_to_boundary[infill_end_point_idx]);
+ }
+
+ const Point interpolate_contour_point(const ContourIntersectionPoint &cp, double param) {
+ const Points &contour = this->boundary[cp.contour_idx];
+ const std::vector &contour_params = this->boundary_params[cp.contour_idx];
+ // Find the start of a contour segment with param.
+ auto it = std::lower_bound(contour_params.begin(), contour_params.end(), param);
+ if (*it != param) {
+ assert(it != contour_params.begin());
+ -- it;
+ }
+ size_t i = it - contour_params.begin();
+ if (i == contour.size())
+ i = 0;
+ double t1 = contour_params[i];
+ double t2 = next_value_modulo(i, contour_params);
+ return lerp(contour[i], next_value_modulo(i, contour), (param - t1) / (t2 - t1));
+ }
+
+ enum Direction {
+ Left,
+ Right,
+ Up,
+ Down,
+ Taken,
+ };
+
+ static Direction dir(const Point &p1, const Point &p2) {
+ return p1.x() == p2.x() ?
+ (p1.y() < p2.y() ? Up : Down) :
+ (p1.x() < p2.x() ? Right : Left);
+ }
+
+ const Direction dir_prev(const ContourIntersectionPoint &cp) const {
+ assert(cp.prev_on_contour);
+ return cp.could_take_prev() ?
+ dir(this->point(cp), this->point(*cp.prev_on_contour)) :
+ Taken;
+ }
+
+ const Direction dir_next(const ContourIntersectionPoint &cp) const {
+ assert(cp.next_on_contour);
+ return cp.could_take_next() ?
+ dir(this->point(cp), this->point(*cp.next_on_contour)) :
+ Taken;
+ }
+
+ bool first(const ContourIntersectionPoint &cp) const {
+ return ((&cp - this->map_infill_end_point_to_boundary.data()) & 1) == 0;
+ }
+
+ const ContourIntersectionPoint& other(const ContourIntersectionPoint &cp) const {
+ return this->map_infill_end_point_to_boundary[((&cp - this->map_infill_end_point_to_boundary.data()) ^ 1)];
+ }
+
+ ContourIntersectionPoint& other(const ContourIntersectionPoint &cp) {
+ return this->map_infill_end_point_to_boundary[((&cp - this->map_infill_end_point_to_boundary.data()) ^ 1)];
+ }
+
+ bool prev_vertical(const ContourIntersectionPoint &cp) const {
+ return this->point(cp).x() == this->point(*cp.prev_on_contour).x();
+ }
+
+ bool next_vertical(const ContourIntersectionPoint &cp) const {
+ return this->point(cp).x() == this->point(*cp.next_on_contour).x();
+ }
+
+};
+
+
+// After mark_boundary_segments_touching_infill() marks boundary segments overlapping trimmed infill lines,
+// there are possibly some very short boundary segments unmarked, but overlapping the untrimmed infill lines fully
+// Mark those short boundary segments.
+static inline void mark_boundary_segments_overlapping_infill(
+ BoundaryInfillGraph &graph,
+ // Infill lines, either completely inside the boundary, or touching the boundary.
+ const Polylines &infill,
+ // Spacing (width) of the infill lines.
+ const double spacing)
+{
+ struct Linef { Vec2d a; Vec2d b; };
+
+ for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
+ const Points &contour = graph.boundary[cp.contour_idx];
+ const std::vector &contour_params = graph.boundary_params[cp.contour_idx];
+ const Polyline &infill_polyline = infill[(&cp - graph.map_infill_end_point_to_boundary.data()) / 2];
+ const double radius = 0.5 * (spacing + SCALED_EPSILON);
+ assert(infill_polyline.size() == 2);
+ const Linef infill_line { infill_polyline.points.front().cast(), infill_polyline.points.back().cast() };
+ if (cp.could_take_next()) {
+ bool inside = true;
+ for (size_t i = cp.point_idx; i != cp.next_on_contour->point_idx; ) {
+ size_t j = next_idx_modulo(i, contour);
+ const Vec2d seg_pt2 = contour[j].cast();
+ if (line_alg::distance_to_squared(infill_line, seg_pt2) < radius * radius) {
+ // The segment is completely inside.
+ } else {
+ std::pair interval;
+ line_rounded_thick_segment_collision(contour[i].cast(), seg_pt2, infill_line.a, infill_line.b, radius, interval);
+ assert(interval.first == 0.);
+ double len_out = closed_contour_distance_ccw(contour_params[cp.point_idx], contour_params[i], contour_params.back()) + interval.second;
+ if (len_out < cp.contour_not_taken_length_next) {
+ // Leaving the infill line region before exiting cp.contour_not_taken_length_next,
+ // thus at least some of the contour is outside and we will extrude this segment.
+ inside = false;
+ break;
+ }
+ }
+ if (closed_contour_distance_ccw(contour_params[cp.point_idx], contour_params[j], contour_params.back()) >= cp.contour_not_taken_length_next)
+ break;
+ i = j;
+ }
+ if (inside) {
+ if (! cp.next_trimmed)
+ // The arc from cp to cp.next_on_contour was not trimmed yet, however it is completely overlapping the infill line.
+ cp.next_on_contour->trim_prev(0);
+ cp.trim_next(0);
+ }
+ } else
+ cp.trim_next(0);
+ if (cp.could_take_prev()) {
+ bool inside = true;
+ for (size_t i = cp.point_idx; i != cp.prev_on_contour->point_idx; ) {
+ size_t j = prev_idx_modulo(i, contour);
+ const Vec2d seg_pt2 = contour[j].cast();
+ // Distance of the second segment line from the infill line.
+ if (line_alg::distance_to_squared(infill_line, seg_pt2) < radius * radius) {
+ // The segment is completely inside.
+ } else {
+ std::pair interval;
+ line_rounded_thick_segment_collision(contour[i].cast(), seg_pt2, infill_line.a, infill_line.b, radius, interval);
+ assert(interval.first == 0.);
+ double len_out = closed_contour_distance_cw(contour_params[cp.point_idx], contour_params[i], contour_params.back()) + interval.second;
+ if (len_out < cp.contour_not_taken_length_prev) {
+ // Leaving the infill line region before exiting cp.contour_not_taken_length_next,
+ // thus at least some of the contour is outside and we will extrude this segment.
+ inside = false;
+ break;
+ }
+ }
+ if (closed_contour_distance_cw(contour_params[cp.point_idx], contour_params[j], contour_params.back()) >= cp.contour_not_taken_length_prev)
+ break;
+ i = j;
+ }
+ if (inside) {
+ if (! cp.prev_trimmed)
+ // The arc from cp to cp.prev_on_contour was not trimmed yet, however it is completely overlapping the infill line.
+ cp.prev_on_contour->trim_next(0);
+ cp.trim_prev(0);
+ }
+ } else
+ cp.trim_prev(0);
+ }
+}
+
+BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered, const std::vector &boundary_src, const BoundingBox &bbox, const double spacing)
+{
+ BoundaryInfillGraph out;
+ out.boundary.assign(boundary_src.size(), Points());
+ out.boundary_params.assign(boundary_src.size(), std::vector());
+ out.map_infill_end_point_to_boundary.assign(infill_ordered.size() * 2, ContourIntersectionPoint{ boundary_idx_unconnected, boundary_idx_unconnected });
+ {
+ // Project the infill_ordered end points onto boundary_src.
+ std::vector> intersection_points;
+ {
+ EdgeGrid::Grid grid;
+ grid.set_bbox(bbox.inflated(SCALED_EPSILON));
+ grid.create(boundary_src, coord_t(scale_(10.)));
+ intersection_points.reserve(infill_ordered.size() * 2);
+ for (const Polyline &pl : infill_ordered)
+ for (const Point *pt : { &pl.points.front(), &pl.points.back() }) {
+ EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point_signed_distance(*pt, coord_t(SCALED_EPSILON));
+ if (cp.valid()) {
+ // The infill end point shall lie on the contour.
+ assert(cp.distance <= 3.);
+ intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1));
+ }
+ }
+ std::sort(intersection_points.begin(), intersection_points.end(), [](const std::pair &cp1, const std::pair &cp2) {
+ return cp1.first.contour_idx < cp2.first.contour_idx ||
+ (cp1.first.contour_idx == cp2.first.contour_idx &&
+ (cp1.first.start_point_idx < cp2.first.start_point_idx ||
+ (cp1.first.start_point_idx == cp2.first.start_point_idx && cp1.first.t < cp2.first.t)));
+ });
+ }
+ auto it = intersection_points.begin();
+ auto it_end = intersection_points.end();
+ std::vector> boundary_intersection_points(out.boundary.size(), std::vector());
+ for (size_t idx_contour = 0; idx_contour < boundary_src.size(); ++ idx_contour) {
// Copy contour_src to contour_dst while adding intersection points.
// Map infill end points map_infill_end_point_to_boundary to the newly inserted boundary points of contour_dst.
// chain the points of map_infill_end_point_to_boundary along their respective contours.
- const Polygon &contour_src = *boundary_src[idx_contour];
- Points &contour_dst = boundary[idx_contour];
+ const Polygon &contour_src = *boundary_src[idx_contour];
+ Points &contour_dst = out.boundary[idx_contour];
std::vector &contour_intersection_points = boundary_intersection_points[idx_contour];
ContourIntersectionPoint *pfirst = nullptr;
ContourIntersectionPoint *pprev = nullptr;
@@ -1164,18 +1320,18 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vectorfirst.contour_idx == idx_contour && it->first.start_point_idx == idx_point; ++ it) {
- // Add these points to the destination contour.
+ contour_dst.emplace_back(ipt);
+ for (; it != it_end && it->first.contour_idx == idx_contour && it->first.start_point_idx == idx_point; ++ it) {
+ // Add these points to the destination contour.
const Polyline &infill_line = infill_ordered[it->second / 2];
const Point &pt = (it->second & 1) ? infill_line.points.back() : infill_line.points.front();
//#ifndef NDEBUG
// {
-// const Vec2d pt1 = ipt.cast();
-// const Vec2d pt2 = (idx_point + 1 == contour_src.size() ? contour_src.points.front() : contour_src.points[idx_point + 1]).cast();
+// const Vec2d pt1 = ipt.cast();
+// const Vec2d pt2 = (idx_point + 1 == contour_src.size() ? contour_src.points.front() : contour_src.points[idx_point + 1]).cast();
// const Vec2d ptx = lerp(pt1, pt2, it->first.t);
// assert(std::abs(ptx.x() - pt.x()) < SCALED_EPSILON);
// assert(std::abs(ptx.y() - pt.y()) < SCALED_EPSILON);
@@ -1187,8 +1343,8 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vectorsecond] = ContourIntersectionPoint{ idx_contour, idx_tjoint_pt };
- ContourIntersectionPoint *pthis = &map_infill_end_point_to_boundary[it->second];
+ out.map_infill_end_point_to_boundary[it->second] = ContourIntersectionPoint{ /* it->second, */ idx_contour, idx_tjoint_pt };
+ ContourIntersectionPoint *pthis = &out.map_infill_end_point_to_boundary[it->second];
if (pprev) {
pprev->next_on_contour = pthis;
pthis->prev_on_contour = pprev;
@@ -1196,15 +1352,15 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vectornext_on_contour = pfirst;
pfirst->prev_on_contour = pprev;
}
- }
- // Parametrize the new boundary with the intersection points inserted.
- std::vector &contour_params = boundary_params[idx_contour];
- contour_params.assign(contour_dst.size() + 1, 0.);
+ }
+ // Parametrize the new boundary with the intersection points inserted.
+ std::vector &contour_params = out.boundary_params[idx_contour];
+ contour_params.assign(contour_dst.size() + 1, 0.);
for (size_t i = 1; i < contour_dst.size(); ++i) {
contour_params[i] = contour_params[i - 1] + (contour_dst[i].cast() - contour_dst[i - 1].cast()).norm();
assert(contour_params[i] > contour_params[i - 1]);
@@ -1225,18 +1381,18 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vectorcontour_not_taken_length_prev = closed_contour_distance_ccw(ip->prev_on_contour->param, ip->param, contour_length);
ip->contour_not_taken_length_next = closed_contour_distance_ccw(ip->param, ip->next_on_contour->param, contour_length);
}
- }
+ }
- assert(boundary.size() == boundary_src.size());
+ assert(out.boundary.size() == boundary_src.size());
#if 0
// Adaptive Cubic Infill produces infill lines, which not always end at the outer boundary.
- assert(std::all_of(map_infill_end_point_to_boundary.begin(), map_infill_end_point_to_boundary.end(),
- [&boundary](const ContourIntersectionPoint &contour_point) {
- return contour_point.contour_idx < boundary.size() && contour_point.point_idx < boundary[contour_point.contour_idx].size();
- }));
+ assert(std::all_of(out.map_infill_end_point_to_boundary.begin(), out.map_infill_end_point_to_boundary.end(),
+ [&out.boundary](const ContourIntersectionPoint &contour_point) {
+ return contour_point.contour_idx < out.boundary.size() && contour_point.point_idx < out.boundary[contour_point.contour_idx].size();
+ }));
#endif
- // Mark the points and segments of split boundary as consumed if they are very close to some of the infill line.
+ // Mark the points and segments of split out.boundary as consumed if they are very close to some of the infill line.
{
// @supermerill used 2. * scale_(spacing)
const double clip_distance = 1.7 * scale_(spacing);
@@ -1244,37 +1400,31 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector merged_with(infill_ordered.size());
+ return out;
+}
+
+void Fill::connect_infill(Polylines &&infill_ordered, const std::vector &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms)
+{
+ assert(! infill_ordered.empty());
+ assert(params.anchor_length >= 0.);
+ assert(params.anchor_length_max >= 0.01f);
+ assert(params.anchor_length_max >= params.anchor_length);
+ const double anchor_length = scale_(params.anchor_length);
+ const double anchor_length_max = scale_(params.anchor_length_max);
+
+#if 0
+ append(polylines_out, infill_ordered);
+ return;
+#endif
+
+ BoundaryInfillGraph graph = create_boundary_infill_graph(infill_ordered, boundary_src, bbox, spacing);
+
+ std::vector merged_with(infill_ordered.size());
std::iota(merged_with.begin(), merged_with.end(), 0);
- struct ConnectionCost {
- ConnectionCost(size_t idx_first, double cost, bool reversed) : idx_first(idx_first), cost(cost), reversed(reversed) {}
- size_t idx_first;
- double cost;
- bool reversed;
- };
- std::vector connections_sorted;
- connections_sorted.reserve(infill_ordered.size() * 2 - 2);
- for (size_t idx_chain = 1; idx_chain < infill_ordered.size(); ++ idx_chain) {
- const ContourIntersectionPoint *cp1 = &map_infill_end_point_to_boundary[(idx_chain - 1) * 2 + 1];
- const ContourIntersectionPoint *cp2 = &map_infill_end_point_to_boundary[idx_chain * 2];
- if (cp1->contour_idx != boundary_idx_unconnected && cp1->contour_idx == cp2->contour_idx) {
- // End points on the same contour. Try to connect them.
- std::pair len = path_lengths_along_contour(cp1, cp2, boundary_params[cp1->contour_idx].back());
- if (len.first < length_max)
- connections_sorted.emplace_back(idx_chain - 1, len.first, false);
- if (len.second < length_max)
- connections_sorted.emplace_back(idx_chain - 1, len.second, true);
- }
- }
- std::sort(connections_sorted.begin(), connections_sorted.end(), [](const ConnectionCost& l, const ConnectionCost& r) { return l.cost < r.cost; });
auto get_and_update_merged_with = [&merged_with](size_t polyline_idx) -> size_t {
for (size_t last = polyline_idx;;) {
@@ -1293,9 +1443,35 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector connections_sorted;
+ connections_sorted.reserve(infill_ordered.size() * 2 - 2);
+ for (size_t idx_chain = 1; idx_chain < infill_ordered.size(); ++ idx_chain) {
+ const ContourIntersectionPoint *cp1 = &graph.map_infill_end_point_to_boundary[(idx_chain - 1) * 2 + 1];
+ const ContourIntersectionPoint *cp2 = &graph.map_infill_end_point_to_boundary[idx_chain * 2];
+ if (cp1->contour_idx != boundary_idx_unconnected && cp1->contour_idx == cp2->contour_idx) {
+ // End points on the same contour. Try to connect them.
+ std::pair len = path_lengths_along_contour(cp1, cp2, graph.boundary_params[cp1->contour_idx].back());
+ if (len.first < length_max)
+ connections_sorted.emplace_back(idx_chain - 1, len.first, false);
+ if (len.second < length_max)
+ connections_sorted.emplace_back(idx_chain - 1, len.second, true);
+ }
+ }
+ std::sort(connections_sorted.begin(), connections_sorted.end(), [](const ConnectionCost& l, const ConnectionCost& r) { return l.cost < r.cost; });
+
for (ConnectionCost &connection_cost : connections_sorted) {
- ContourIntersectionPoint *cp1 = &map_infill_end_point_to_boundary[connection_cost.idx_first * 2 + 1];
- ContourIntersectionPoint *cp2 = &map_infill_end_point_to_boundary[(connection_cost.idx_first + 1) * 2];
+ ContourIntersectionPoint *cp1 = &graph.map_infill_end_point_to_boundary[connection_cost.idx_first * 2 + 1];
+ ContourIntersectionPoint *cp2 = &graph.map_infill_end_point_to_boundary[(connection_cost.idx_first + 1) * 2];
assert(cp1 != cp2);
assert(cp1->contour_idx == cp2->contour_idx && cp1->contour_idx != boundary_idx_unconnected);
if (cp1->consumed || cp2->consumed)
@@ -1306,7 +1482,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vectorparam, cp_high->param, boundary_params[cp1->contour_idx].back())) < SCALED_EPSILON);
+ assert(std::abs(length - closed_contour_distance_ccw(cp_low->param, cp_high->param, graph.boundary_params[cp1->contour_idx].back())) < SCALED_EPSILON);
could_connect = ! cp_low->next_trimmed && ! cp_high->prev_trimmed;
if (could_connect && cp_low->next_on_contour != cp_high) {
// Other end of cp1, may or may not be on the same contour as cp1.
@@ -1329,14 +1505,14 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vectorcontour_idx], cp1, cp2, connection_cost.reversed);
+ take(infill_ordered[idx_first], infill_ordered[idx_second], graph.boundary[cp1->contour_idx], cp1, cp2, connection_cost.reversed);
// Mark the second polygon as merged with the first one.
merged_with[idx_second] = merged_with[idx_first];
infill_ordered[idx_second].points.clear();
} else {
// Try to connect cp1 resp. cp2 with a piece of perimeter line.
- take_limited(infill_ordered[idx_first], boundary[cp1->contour_idx], boundary_params[cp1->contour_idx], cp1, cp2, connection_cost.reversed, anchor_length, line_half_width);
- take_limited(infill_ordered[idx_second], boundary[cp1->contour_idx], boundary_params[cp1->contour_idx], cp2, cp1, ! connection_cost.reversed, anchor_length, line_half_width);
+ take_limited(infill_ordered[idx_first], graph.boundary[cp1->contour_idx], graph.boundary_params[cp1->contour_idx], cp1, cp2, connection_cost.reversed, anchor_length, line_half_width);
+ take_limited(infill_ordered[idx_second], graph.boundary[cp1->contour_idx], graph.boundary_params[cp1->contour_idx], cp2, cp1, ! connection_cost.reversed, anchor_length, line_half_width);
}
}
#endif
@@ -1346,10 +1522,10 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector arches;
- arches.reserve(map_infill_end_point_to_boundary.size());
- for (ContourIntersectionPoint &cp : map_infill_end_point_to_boundary)
+ arches.reserve(graph.map_infill_end_point_to_boundary.size());
+ for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary)
if (cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
- arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, boundary_params[cp.contour_idx].back()) });
+ arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, graph.boundary_params[cp.contour_idx].back()) });
std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length < r.arc_length; });
//FIXME improve the Traveling Salesman problem with 2-opt and 3-opt local optimization.
@@ -1358,10 +1534,10 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vectornext_on_contour;
- size_t polyline_idx1 = get_and_update_merged_with(((cp1 - map_infill_end_point_to_boundary.data()) / 2));
- size_t polyline_idx2 = get_and_update_merged_with(((cp2 - map_infill_end_point_to_boundary.data()) / 2));
- const Points &contour = boundary[cp1->contour_idx];
- const std::vector &contour_params = boundary_params[cp1->contour_idx];
+ size_t polyline_idx1 = get_and_update_merged_with(((cp1 - graph.map_infill_end_point_to_boundary.data()) / 2));
+ size_t polyline_idx2 = get_and_update_merged_with(((cp2 - graph.map_infill_end_point_to_boundary.data()) / 2));
+ const Points &contour = graph.boundary[cp1->contour_idx];
+ const std::vector &contour_params = graph.boundary_params[cp1->contour_idx];
if (polyline_idx1 != polyline_idx2) {
Polyline &polyline1 = infill_ordered[polyline_idx1];
Polyline &polyline2 = infill_ordered[polyline_idx2];
@@ -1392,10 +1568,10 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector