diff --git a/resources/images/param_crosszag.svg b/resources/images/param_crosszag.svg
new file mode 100644
index 0000000000..0a21257317
--- /dev/null
+++ b/resources/images/param_crosszag.svg
@@ -0,0 +1,44 @@
+
diff --git a/resources/images/param_lockedzag.svg b/resources/images/param_lockedzag.svg
new file mode 100644
index 0000000000..55e87074ff
--- /dev/null
+++ b/resources/images/param_lockedzag.svg
@@ -0,0 +1,29 @@
+
diff --git a/resources/images/param_zigzag.svg b/resources/images/param_zigzag.svg
new file mode 100644
index 0000000000..f762e99c9a
--- /dev/null
+++ b/resources/images/param_zigzag.svg
@@ -0,0 +1,158 @@
+
+
diff --git a/resources/profiles/BBL.json b/resources/profiles/BBL.json
index 10383cb01e..9ac95694b3 100644
--- a/resources/profiles/BBL.json
+++ b/resources/profiles/BBL.json
@@ -1,7 +1,7 @@
{
"name": "Bambulab",
"url": "http://www.bambulab.com/Parameters/vendor/BBL.json",
- "version": "01.10.00.36",
+ "version": "02.00.00.53",
"force_update": "0",
"description": "the initial version of BBL configurations",
"machine_model_list": [
diff --git a/resources/profiles/BBL/process/fdm_process_common.json b/resources/profiles/BBL/process/fdm_process_common.json
index 9b99ec1b28..8a9c65ad17 100644
--- a/resources/profiles/BBL/process/fdm_process_common.json
+++ b/resources/profiles/BBL/process/fdm_process_common.json
@@ -71,5 +71,8 @@
"compatible_printers": [],
"smooth_coefficient": "80",
"overhang_totally_speed": "19",
- "scarf_angle_threshold": "155"
+ "scarf_angle_threshold": "155",
+ "infill_shift_step": "0.4",
+ "infill_rotate_step": "0",
+ "symmetric_infill_y_axis": "false"
}
\ No newline at end of file
diff --git a/src/libslic3r/BoundingBox.cpp b/src/libslic3r/BoundingBox.cpp
index f2a258797b..a2a510b64c 100644
--- a/src/libslic3r/BoundingBox.cpp
+++ b/src/libslic3r/BoundingBox.cpp
@@ -49,6 +49,13 @@ BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const
return out;
}
+BoundingBox BoundingBox::scaled(double factor) const
+{
+ BoundingBox out(*this);
+ out.scale(factor);
+ return out;
+}
+
template void
BoundingBoxBase::scale(double factor)
{
diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp
index 9c305bd9ad..d6baa27012 100644
--- a/src/libslic3r/BoundingBox.hpp
+++ b/src/libslic3r/BoundingBox.hpp
@@ -222,7 +222,9 @@ public:
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {}
BoundingBox(const Points &points) : BoundingBoxBase(points) {}
- BoundingBox inflated(coordf_t delta) const throw() { BoundingBox out(*this); out.offset(delta); return out; }
+ BoundingBox inflated(coordf_t delta) const noexcept { BoundingBox out(*this); out.offset(delta); return out; }
+
+ BoundingBox scaled(double factor) const;
friend BoundingBox get_extents_rotated(const Points &points, double angle);
};
diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp
index 3ce062ac39..d5332688ab 100644
--- a/src/libslic3r/Config.hpp
+++ b/src/libslic3r/Config.hpp
@@ -657,6 +657,11 @@ public:
{
if (! append)
this->values.clear();
+
+ if (str.empty()) {
+ this->values.push_back(0);
+ return true;
+ }
std::istringstream is(str);
std::string item_str;
while (std::getline(is, item_str, ',')) {
@@ -675,6 +680,13 @@ public:
}
return true;
}
+ static bool validate_string(const std::string &str)
+ {
+ // should only have number and commas
+ return std::all_of(str.begin(), str.end(), [](char c) {
+ return std::isdigit(c) || c == ','|| std::isspace(c);
+ });
+ }
ConfigOptionFloatsTempl& operator=(const ConfigOption *opt)
{
diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp
index f2dbf7389a..185e925084 100644
--- a/src/libslic3r/ExPolygon.cpp
+++ b/src/libslic3r/ExPolygon.cpp
@@ -1,544 +1,551 @@
-#include "BoundingBox.hpp"
-#include "ExPolygon.hpp"
-#include "Exception.hpp"
-#include "Geometry/MedialAxis.hpp"
-#include "Polygon.hpp"
-#include "Line.hpp"
-#include "ClipperUtils.hpp"
-#include "SVG.hpp"
-#include
-#include
-#include
-
-namespace Slic3r {
-
-void ExPolygon::scale(double factor)
-{
- contour.scale(factor);
- for (Polygon &hole : holes)
- hole.scale(factor);
-}
-
-void ExPolygon::scale(double factor_x, double factor_y)
-{
- contour.scale(factor_x, factor_y);
- for (Polygon &hole : holes)
- hole.scale(factor_x, factor_y);
-}
-
-void ExPolygon::translate(const Point &p)
-{
- contour.translate(p);
- for (Polygon &hole : holes)
- hole.translate(p);
-}
-
-void ExPolygon::rotate(double angle)
-{
- contour.rotate(angle);
- for (Polygon &hole : holes)
- hole.rotate(angle);
-}
-
-void ExPolygon::rotate(double angle, const Point ¢er)
-{
- contour.rotate(angle, center);
- for (Polygon &hole : holes)
- hole.rotate(angle, center);
-}
-
-double ExPolygon::area() const
-{
- double a = this->contour.area();
- for (const Polygon &hole : holes)
- a -= - hole.area(); // holes have negative area
- return a;
-}
-
-bool ExPolygon::is_valid() const
-{
- if (!this->contour.is_valid() || !this->contour.is_counter_clockwise()) return false;
- for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
- if (!(*it).is_valid() || (*it).is_counter_clockwise()) return false;
- }
- return true;
-}
-
-void ExPolygon::douglas_peucker(double tolerance)
-{
- this->contour.douglas_peucker(tolerance);
- for (Polygon &poly : this->holes)
- poly.douglas_peucker(tolerance);
-}
-
-bool ExPolygon::contains(const Line &line) const
-{
- return this->contains(Polyline(line.a, line.b));
-}
-
-bool ExPolygon::contains(const Polyline &polyline) const
-{
- BoundingBox bbox1 = get_extents(*this);
- BoundingBox bbox2 = get_extents(polyline);
- bbox2.inflated(1);
- if (!bbox1.overlap(bbox2))
- return false;
-
- return diff_pl(polyline, *this).empty();
-}
-
-bool ExPolygon::contains(const Polylines &polylines) const
-{
- #if 0
- BoundingBox bbox = get_extents(polylines);
- bbox.merge(get_extents(*this));
- SVG svg(debug_out_path("ExPolygon_contains.svg"), bbox);
- svg.draw(*this);
- svg.draw_outline(*this);
- svg.draw(polylines, "blue");
- #endif
- Polylines pl_out = diff_pl(polylines, *this);
- #if 0
- svg.draw(pl_out, "red");
- #endif
- return pl_out.empty();
-}
-
-bool ExPolygon::contains(const Point &point, bool border_result /* = true */) const
-{
- if (! Slic3r::contains(contour, point, border_result))
- // Outside the outer contour, not on the contour boundary.
- return false;
- for (const Polygon &hole : this->holes)
- if (Slic3r::contains(hole, point, ! border_result))
- // Inside a hole, not on the hole boundary.
- return false;
- return true;
-}
-
-bool ExPolygon::on_boundary(const Point &point, double eps) const
-{
- if (this->contour.on_boundary(point, eps))
- return true;
- for (const Polygon &hole : this->holes)
- if (hole.on_boundary(point, eps))
- return true;
- return false;
-}
-
-// Projection of a point onto the polygon.
-Point ExPolygon::point_projection(const Point &point) const
-{
- if (this->holes.empty()) {
- return this->contour.point_projection(point);
- } else {
- double dist_min2 = std::numeric_limits::max();
- Point closest_pt_min;
- for (size_t i = 0; i < this->num_contours(); ++ i) {
- Point closest_pt = this->contour_or_hole(i).point_projection(point);
- double d2 = (closest_pt - point).cast().squaredNorm();
- if (d2 < dist_min2) {
- dist_min2 = d2;
- closest_pt_min = closest_pt;
- }
- }
- return closest_pt_min;
- }
-}
-
-bool ExPolygon::overlaps(const ExPolygon &other) const
-{
- if (this->empty() || other.empty())
- return false;
-
- #if 0
- BoundingBox bbox = get_extents(other);
- bbox.merge(get_extents(*this));
- static int iRun = 0;
- SVG svg(debug_out_path("ExPolygon_overlaps-%d.svg", iRun ++), bbox);
- svg.draw(*this);
- svg.draw_outline(*this);
- svg.draw_outline(other, "blue");
- #endif
-
- Polylines pl_out = intersection_pl(to_polylines(other), *this);
-
- #if 0
- svg.draw(pl_out, "red");
- #endif
-
- // See unit test SCENARIO("Clipper diff with polyline", "[Clipper]")
- // for in which case the intersection_pl produces any intersection.
- return ! pl_out.empty() ||
- // If *this is completely inside other, then pl_out is empty, but the expolygons overlap. Test for that situation.
- other.contains(this->contour.points.front());
-}
-
-bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2)
-{
- for (const ExPolygon& expoly1 : expolys1) {
- for (const ExPolygon& expoly2 : expolys2) {
- if (expoly1.overlaps(expoly2))
- return true;
- }
- }
- return false;
-}
-
-bool overlaps(const ExPolygons& expolys, const ExPolygon& expoly)
-{
- for (const ExPolygon& el : expolys) {
- if (el.overlaps(expoly))
- return true;
- }
- return false;
-}
-
-Point projection_onto(const ExPolygons& polygons, const Point& from)
-{
- Point projected_pt;
- double min_dist = std::numeric_limits::max();
-
- for (const auto& poly : polygons) {
- for (int i = 0; i < poly.num_contours(); i++) {
- Point p = from.projection_onto(poly.contour_or_hole(i));
- double dist = (from - p).cast().squaredNorm();
- if (dist < min_dist) {
- projected_pt = p;
- min_dist = dist;
- }
- }
- }
-
- return projected_pt;
-}
-
-void ExPolygon::simplify_p(double tolerance, Polygons* polygons) const
-{
- Polygons pp = this->simplify_p(tolerance);
- polygons->insert(polygons->end(), pp.begin(), pp.end());
-}
-
-Polygons ExPolygon::simplify_p(double tolerance) const
-{
- Polygons pp;
- pp.reserve(this->holes.size() + 1);
- // contour
- {
- Polygon p = this->contour;
- p.points.push_back(p.points.front());
- p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
- p.points.pop_back();
- pp.emplace_back(std::move(p));
- }
- // holes
- for (Polygon p : this->holes) {
- p.points.push_back(p.points.front());
- p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
- p.points.pop_back();
- pp.emplace_back(std::move(p));
- }
- return simplify_polygons(pp);
-}
-
-ExPolygons ExPolygon::simplify(double tolerance) const
-{
- return union_ex(this->simplify_p(tolerance));
-}
-
-void ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const
-{
- append(*expolygons, this->simplify(tolerance));
-}
-
-void ExPolygon::medial_axis(double min_width, double max_width, ThickPolylines* polylines) const
-{
- // init helper object
- Slic3r::Geometry::MedialAxis ma(min_width, max_width, *this);
-
- // compute the Voronoi diagram and extract medial axis polylines
- ThickPolylines pp;
- ma.build(&pp);
-
- /*
- SVG svg("medial_axis.svg");
- svg.draw(*this);
- svg.draw(pp);
- svg.Close();
- */
-
- /* Find the maximum width returned; we're going to use this for validating and
- filtering the output segments. */
- double max_w = 0;
- for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it)
- max_w = fmaxf(max_w, *std::max_element(it->width.begin(), it->width.end()));
-
- /* Loop through all returned polylines in order to extend their endpoints to the
- expolygon boundaries */
- bool removed = false;
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
-
- // extend initial and final segments of each polyline if they're actual endpoints
- /* We assign new endpoints to temporary variables because in case of a single-line
- polyline, after we extend the start point it will be caught by the intersection()
- call, so we keep the inner point until we perform the second intersection() as well */
- Point new_front = polyline.points.front();
- Point new_back = polyline.points.back();
- if (polyline.endpoints.first && !this->on_boundary(new_front, SCALED_EPSILON)) {
- Vec2d p1 = polyline.points.front().cast();
- Vec2d p2 = polyline.points[1].cast();
- // prevent the line from touching on the other side, otherwise intersection() might return that solution
- if (polyline.points.size() == 2)
- p2 = (p1 + p2) * 0.5;
- // Extend the start of the segment.
- p1 -= (p2 - p1).normalized() * max_width;
- this->contour.intersection(Line(p1.cast(), p2.cast()), &new_front);
- }
- if (polyline.endpoints.second && !this->on_boundary(new_back, SCALED_EPSILON)) {
- Vec2d p1 = (polyline.points.end() - 2)->cast();
- Vec2d p2 = polyline.points.back().cast();
- // prevent the line from touching on the other side, otherwise intersection() might return that solution
- if (polyline.points.size() == 2)
- p1 = (p1 + p2) * 0.5;
- // Extend the start of the segment.
- p2 += (p2 - p1).normalized() * max_width;
- this->contour.intersection(Line(p1.cast(), p2.cast()), &new_back);
- }
- polyline.points.front() = new_front;
- polyline.points.back() = new_back;
-
- /* remove too short polylines
- (we can't do this check before endpoints extension and clipping because we don't
- know how long will the endpoints be extended since it depends on polygon thickness
- which is variable - extension will be <= max_width/2 on each side) */
- if ((polyline.endpoints.first || polyline.endpoints.second)
- && polyline.length() < max_w*2) {
- pp.erase(pp.begin() + i);
- --i;
- removed = true;
- continue;
- }
- }
-
- /* If we removed any short polylines we now try to connect consecutive polylines
- in order to allow loop detection. Note that this algorithm is greedier than
- MedialAxis::process_edge_neighbors() as it will connect random pairs of
- polylines even when more than two start from the same point. This has no
- drawbacks since we optimize later using nearest-neighbor which would do the
- same, but should we use a more sophisticated optimization algorithm we should
- not connect polylines when more than two meet. */
- if (removed) {
- for (size_t i = 0; i < pp.size(); ++i) {
- ThickPolyline& polyline = pp[i];
- if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization
-
- // find another polyline starting here
- for (size_t j = i+1; j < pp.size(); ++j) {
- ThickPolyline& other = pp[j];
- if (polyline.last_point() == other.last_point()) {
- other.reverse();
- } else if (polyline.first_point() == other.last_point()) {
- polyline.reverse();
- other.reverse();
- } else if (polyline.first_point() == other.first_point()) {
- polyline.reverse();
- } else if (polyline.last_point() != other.first_point()) {
- continue;
- }
-
- polyline.points.insert(polyline.points.end(), other.points.begin() + 1, other.points.end());
- polyline.width.insert(polyline.width.end(), other.width.begin(), other.width.end());
- polyline.endpoints.second = other.endpoints.second;
- assert(polyline.width.size() == polyline.points.size()*2 - 2);
-
- pp.erase(pp.begin() + j);
- j = i; // restart search from i+1
- }
- }
- }
-
- polylines->insert(polylines->end(), pp.begin(), pp.end());
-}
-
-void ExPolygon::medial_axis(double min_width, double max_width, Polylines* polylines) const
-{
- ThickPolylines tp;
- this->medial_axis(min_width, max_width, &tp);
- polylines->reserve(polylines->size() + tp.size());
- for (auto &pl : tp)
- polylines->emplace_back(pl.points);
-}
-
-Lines ExPolygon::lines() const
-{
- Lines lines = this->contour.lines();
- for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
- Lines hole_lines = h->lines();
- lines.insert(lines.end(), hole_lines.begin(), hole_lines.end());
- }
- return lines;
-}
-
-// Do expolygons match? If they match, they must have the same topology,
-// however their contours may be rotated.
-bool expolygons_match(const ExPolygon &l, const ExPolygon &r)
-{
- if (l.holes.size() != r.holes.size() || ! polygons_match(l.contour, r.contour))
- return false;
- for (size_t hole_idx = 0; hole_idx < l.holes.size(); ++ hole_idx)
- if (! polygons_match(l.holes[hole_idx], r.holes[hole_idx]))
- return false;
- return true;
-}
-
-BoundingBox get_extents(const ExPolygon &expolygon)
-{
- return get_extents(expolygon.contour);
-}
-
-BoundingBox get_extents(const ExPolygons &expolygons)
-{
- BoundingBox bbox;
- if (! expolygons.empty()) {
- for (size_t i = 0; i < expolygons.size(); ++ i)
- if (! expolygons[i].contour.points.empty())
- bbox.merge(get_extents(expolygons[i]));
- }
- return bbox;
-}
-
-BoundingBox get_extents_rotated(const ExPolygon &expolygon, double angle)
-{
- return get_extents_rotated(expolygon.contour, angle);
-}
-
-BoundingBox get_extents_rotated(const ExPolygons &expolygons, double angle)
-{
- BoundingBox bbox;
- if (! expolygons.empty()) {
- bbox = get_extents_rotated(expolygons.front().contour, angle);
- for (size_t i = 1; i < expolygons.size(); ++ i)
- bbox.merge(get_extents_rotated(expolygons[i].contour, angle));
- }
- return bbox;
-}
-
-extern std::vector get_extents_vector(const ExPolygons &polygons)
-{
- std::vector out;
- out.reserve(polygons.size());
- for (ExPolygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
- out.push_back(get_extents(*it));
- return out;
-}
-
-bool has_duplicate_points(const ExPolygon &expoly)
-{
-#if 1
- // Check globally.
- size_t cnt = expoly.contour.points.size();
- for (const Polygon &hole : expoly.holes)
- cnt += hole.points.size();
- Points allpts;
- allpts.reserve(cnt);
- allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
- for (const Polygon &hole : expoly.holes)
- allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
- return has_duplicate_points(std::move(allpts));
-#else
- // Check per contour.
- if (has_duplicate_points(expoly.contour))
- return true;
- for (const Polygon &hole : expoly.holes)
- if (has_duplicate_points(hole))
- return true;
- return false;
-#endif
-}
-
-bool has_duplicate_points(const ExPolygons &expolys)
-{
-#if 1
- // Check globally.
- Points allpts;
- allpts.reserve(count_points(expolys));
- for (const ExPolygon &expoly : expolys) {
- allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
- for (const Polygon &hole : expoly.holes)
- allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
- }
- return has_duplicate_points(std::move(allpts));
-#else
- // Check per contour.
- for (const ExPolygon &expoly : expolys)
- if (has_duplicate_points(expoly))
- return true;
- return false;
-#endif
-}
-
-bool remove_same_neighbor(ExPolygons &expolygons)
-{
- if (expolygons.empty())
- return false;
- bool remove_from_holes = false;
- bool remove_from_contour = false;
- for (ExPolygon &expoly : expolygons) {
- remove_from_contour |= remove_same_neighbor(expoly.contour);
- remove_from_holes |= remove_same_neighbor(expoly.holes);
- }
- // Removing of expolygons without contour
- if (remove_from_contour)
- expolygons.erase(std::remove_if(expolygons.begin(), expolygons.end(),
- [](const ExPolygon &p) { return p.contour.points.size() <= 2; }),
- expolygons.end());
- return remove_from_holes || remove_from_contour;
-}
-
-bool remove_sticks(ExPolygon &poly)
-{
- return remove_sticks(poly.contour) || remove_sticks(poly.holes);
-}
-
-bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area)
-{
- bool modified = false;
- size_t free_idx = 0;
- for (size_t expoly_idx = 0; expoly_idx < expolygons.size(); ++expoly_idx) {
- if (std::abs(expolygons[expoly_idx].area()) >= min_area) {
- // Expolygon is big enough, so also check all its holes
- modified |= remove_small(expolygons[expoly_idx].holes, min_area);
- if (free_idx < expoly_idx) {
- std::swap(expolygons[expoly_idx].contour, expolygons[free_idx].contour);
- std::swap(expolygons[expoly_idx].holes, expolygons[free_idx].holes);
- }
- ++free_idx;
- } else
- modified = true;
- }
- if (free_idx < expolygons.size())
- expolygons.erase(expolygons.begin() + free_idx, expolygons.end());
- return modified;
-}
-
-void keep_largest_contour_only(ExPolygons &polygons)
-{
- if (polygons.size() > 1) {
- double max_area = 0.;
- ExPolygon* max_area_polygon = nullptr;
- for (ExPolygon& p : polygons) {
- double a = p.contour.area();
- if (a > max_area) {
- max_area = a;
- max_area_polygon = &p;
- }
- }
- assert(max_area_polygon != nullptr);
- ExPolygon p(std::move(*max_area_polygon));
- polygons.clear();
- polygons.emplace_back(std::move(p));
- }
-}
-
-} // namespace Slic3r
+#include "BoundingBox.hpp"
+#include "ExPolygon.hpp"
+#include "Exception.hpp"
+#include "Geometry/MedialAxis.hpp"
+#include "Polygon.hpp"
+#include "Line.hpp"
+#include "ClipperUtils.hpp"
+#include "SVG.hpp"
+#include
+#include
+#include
+
+namespace Slic3r {
+
+void ExPolygon::scale(double factor)
+{
+ contour.scale(factor);
+ for (Polygon &hole : holes)
+ hole.scale(factor);
+}
+
+void ExPolygon::scale(double factor_x, double factor_y)
+{
+ contour.scale(factor_x, factor_y);
+ for (Polygon &hole : holes)
+ hole.scale(factor_x, factor_y);
+}
+
+void ExPolygon::translate(const Point &p)
+{
+ contour.translate(p);
+ for (Polygon &hole : holes)
+ hole.translate(p);
+}
+
+void ExPolygon::rotate(double angle)
+{
+ contour.rotate(angle);
+ for (Polygon &hole : holes)
+ hole.rotate(angle);
+}
+
+void ExPolygon::rotate(double angle, const Point ¢er)
+{
+ contour.rotate(angle, center);
+ for (Polygon &hole : holes)
+ hole.rotate(angle, center);
+}
+
+double ExPolygon::area() const
+{
+ double a = this->contour.area();
+ for (const Polygon &hole : holes)
+ a -= - hole.area(); // holes have negative area
+ return a;
+}
+
+bool ExPolygon::is_valid() const
+{
+ if (!this->contour.is_valid() || !this->contour.is_counter_clockwise()) return false;
+ for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
+ if (!(*it).is_valid() || (*it).is_counter_clockwise()) return false;
+ }
+ return true;
+}
+
+void ExPolygon::douglas_peucker(double tolerance)
+{
+ this->contour.douglas_peucker(tolerance);
+ for (Polygon &poly : this->holes)
+ poly.douglas_peucker(tolerance);
+}
+
+bool ExPolygon::contains(const Line &line) const
+{
+ return this->contains(Polyline(line.a, line.b));
+}
+
+bool ExPolygon::contains(const Polyline &polyline) const
+{
+ BoundingBox bbox1 = get_extents(*this);
+ BoundingBox bbox2 = get_extents(polyline);
+ bbox2.inflated(1);
+ if (!bbox1.overlap(bbox2))
+ return false;
+
+ return diff_pl(polyline, *this).empty();
+}
+
+bool ExPolygon::contains(const Polylines &polylines) const
+{
+ #if 0
+ BoundingBox bbox = get_extents(polylines);
+ bbox.merge(get_extents(*this));
+ SVG svg(debug_out_path("ExPolygon_contains.svg"), bbox);
+ svg.draw(*this);
+ svg.draw_outline(*this);
+ svg.draw(polylines, "blue");
+ #endif
+ Polylines pl_out = diff_pl(polylines, *this);
+ #if 0
+ svg.draw(pl_out, "red");
+ #endif
+ return pl_out.empty();
+}
+
+bool ExPolygon::contains(const Point &point, bool border_result /* = true */) const
+{
+ if (! Slic3r::contains(contour, point, border_result))
+ // Outside the outer contour, not on the contour boundary.
+ return false;
+ for (const Polygon &hole : this->holes)
+ if (Slic3r::contains(hole, point, ! border_result))
+ // Inside a hole, not on the hole boundary.
+ return false;
+ return true;
+}
+
+bool ExPolygon::on_boundary(const Point &point, double eps) const
+{
+ if (this->contour.on_boundary(point, eps))
+ return true;
+ for (const Polygon &hole : this->holes)
+ if (hole.on_boundary(point, eps))
+ return true;
+ return false;
+}
+
+// Projection of a point onto the polygon.
+Point ExPolygon::point_projection(const Point &point) const
+{
+ if (this->holes.empty()) {
+ return this->contour.point_projection(point);
+ } else {
+ double dist_min2 = std::numeric_limits::max();
+ Point closest_pt_min;
+ for (size_t i = 0; i < this->num_contours(); ++ i) {
+ Point closest_pt = this->contour_or_hole(i).point_projection(point);
+ double d2 = (closest_pt - point).cast().squaredNorm();
+ if (d2 < dist_min2) {
+ dist_min2 = d2;
+ closest_pt_min = closest_pt;
+ }
+ }
+ return closest_pt_min;
+ }
+}
+
+void ExPolygon::symmetric_y(const coord_t &y_axis)
+{
+ this->contour.symmetric_y(y_axis);
+ for (Polygon &hole : holes)
+ hole.symmetric_y(y_axis);
+}
+
+bool ExPolygon::overlaps(const ExPolygon &other) const
+{
+ if (this->empty() || other.empty())
+ return false;
+
+ #if 0
+ BoundingBox bbox = get_extents(other);
+ bbox.merge(get_extents(*this));
+ static int iRun = 0;
+ SVG svg(debug_out_path("ExPolygon_overlaps-%d.svg", iRun ++), bbox);
+ svg.draw(*this);
+ svg.draw_outline(*this);
+ svg.draw_outline(other, "blue");
+ #endif
+
+ Polylines pl_out = intersection_pl(to_polylines(other), *this);
+
+ #if 0
+ svg.draw(pl_out, "red");
+ #endif
+
+ // See unit test SCENARIO("Clipper diff with polyline", "[Clipper]")
+ // for in which case the intersection_pl produces any intersection.
+ return ! pl_out.empty() ||
+ // If *this is completely inside other, then pl_out is empty, but the expolygons overlap. Test for that situation.
+ other.contains(this->contour.points.front());
+}
+
+bool overlaps(const ExPolygons& expolys1, const ExPolygons& expolys2)
+{
+ for (const ExPolygon& expoly1 : expolys1) {
+ for (const ExPolygon& expoly2 : expolys2) {
+ if (expoly1.overlaps(expoly2))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool overlaps(const ExPolygons& expolys, const ExPolygon& expoly)
+{
+ for (const ExPolygon& el : expolys) {
+ if (el.overlaps(expoly))
+ return true;
+ }
+ return false;
+}
+
+Point projection_onto(const ExPolygons& polygons, const Point& from)
+{
+ Point projected_pt;
+ double min_dist = std::numeric_limits::max();
+
+ for (const auto& poly : polygons) {
+ for (int i = 0; i < poly.num_contours(); i++) {
+ Point p = from.projection_onto(poly.contour_or_hole(i));
+ double dist = (from - p).cast().squaredNorm();
+ if (dist < min_dist) {
+ projected_pt = p;
+ min_dist = dist;
+ }
+ }
+ }
+
+ return projected_pt;
+}
+
+void ExPolygon::simplify_p(double tolerance, Polygons* polygons) const
+{
+ Polygons pp = this->simplify_p(tolerance);
+ polygons->insert(polygons->end(), pp.begin(), pp.end());
+}
+
+Polygons ExPolygon::simplify_p(double tolerance) const
+{
+ Polygons pp;
+ pp.reserve(this->holes.size() + 1);
+ // contour
+ {
+ Polygon p = this->contour;
+ p.points.push_back(p.points.front());
+ p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
+ p.points.pop_back();
+ pp.emplace_back(std::move(p));
+ }
+ // holes
+ for (Polygon p : this->holes) {
+ p.points.push_back(p.points.front());
+ p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
+ p.points.pop_back();
+ pp.emplace_back(std::move(p));
+ }
+ return simplify_polygons(pp);
+}
+
+ExPolygons ExPolygon::simplify(double tolerance) const
+{
+ return union_ex(this->simplify_p(tolerance));
+}
+
+void ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const
+{
+ append(*expolygons, this->simplify(tolerance));
+}
+
+void ExPolygon::medial_axis(double min_width, double max_width, ThickPolylines* polylines) const
+{
+ // init helper object
+ Slic3r::Geometry::MedialAxis ma(min_width, max_width, *this);
+
+ // compute the Voronoi diagram and extract medial axis polylines
+ ThickPolylines pp;
+ ma.build(&pp);
+
+ /*
+ SVG svg("medial_axis.svg");
+ svg.draw(*this);
+ svg.draw(pp);
+ svg.Close();
+ */
+
+ /* Find the maximum width returned; we're going to use this for validating and
+ filtering the output segments. */
+ double max_w = 0;
+ for (ThickPolylines::const_iterator it = pp.begin(); it != pp.end(); ++it)
+ max_w = fmaxf(max_w, *std::max_element(it->width.begin(), it->width.end()));
+
+ /* Loop through all returned polylines in order to extend their endpoints to the
+ expolygon boundaries */
+ bool removed = false;
+ for (size_t i = 0; i < pp.size(); ++i) {
+ ThickPolyline& polyline = pp[i];
+
+ // extend initial and final segments of each polyline if they're actual endpoints
+ /* We assign new endpoints to temporary variables because in case of a single-line
+ polyline, after we extend the start point it will be caught by the intersection()
+ call, so we keep the inner point until we perform the second intersection() as well */
+ Point new_front = polyline.points.front();
+ Point new_back = polyline.points.back();
+ if (polyline.endpoints.first && !this->on_boundary(new_front, SCALED_EPSILON)) {
+ Vec2d p1 = polyline.points.front().cast();
+ Vec2d p2 = polyline.points[1].cast();
+ // prevent the line from touching on the other side, otherwise intersection() might return that solution
+ if (polyline.points.size() == 2)
+ p2 = (p1 + p2) * 0.5;
+ // Extend the start of the segment.
+ p1 -= (p2 - p1).normalized() * max_width;
+ this->contour.intersection(Line(p1.cast(), p2.cast()), &new_front);
+ }
+ if (polyline.endpoints.second && !this->on_boundary(new_back, SCALED_EPSILON)) {
+ Vec2d p1 = (polyline.points.end() - 2)->cast();
+ Vec2d p2 = polyline.points.back().cast();
+ // prevent the line from touching on the other side, otherwise intersection() might return that solution
+ if (polyline.points.size() == 2)
+ p1 = (p1 + p2) * 0.5;
+ // Extend the start of the segment.
+ p2 += (p2 - p1).normalized() * max_width;
+ this->contour.intersection(Line(p1.cast(), p2.cast()), &new_back);
+ }
+ polyline.points.front() = new_front;
+ polyline.points.back() = new_back;
+
+ /* remove too short polylines
+ (we can't do this check before endpoints extension and clipping because we don't
+ know how long will the endpoints be extended since it depends on polygon thickness
+ which is variable - extension will be <= max_width/2 on each side) */
+ if ((polyline.endpoints.first || polyline.endpoints.second)
+ && polyline.length() < max_w*2) {
+ pp.erase(pp.begin() + i);
+ --i;
+ removed = true;
+ continue;
+ }
+ }
+
+ /* If we removed any short polylines we now try to connect consecutive polylines
+ in order to allow loop detection. Note that this algorithm is greedier than
+ MedialAxis::process_edge_neighbors() as it will connect random pairs of
+ polylines even when more than two start from the same point. This has no
+ drawbacks since we optimize later using nearest-neighbor which would do the
+ same, but should we use a more sophisticated optimization algorithm we should
+ not connect polylines when more than two meet. */
+ if (removed) {
+ for (size_t i = 0; i < pp.size(); ++i) {
+ ThickPolyline& polyline = pp[i];
+ if (polyline.endpoints.first && polyline.endpoints.second) continue; // optimization
+
+ // find another polyline starting here
+ for (size_t j = i+1; j < pp.size(); ++j) {
+ ThickPolyline& other = pp[j];
+ if (polyline.last_point() == other.last_point()) {
+ other.reverse();
+ } else if (polyline.first_point() == other.last_point()) {
+ polyline.reverse();
+ other.reverse();
+ } else if (polyline.first_point() == other.first_point()) {
+ polyline.reverse();
+ } else if (polyline.last_point() != other.first_point()) {
+ continue;
+ }
+
+ polyline.points.insert(polyline.points.end(), other.points.begin() + 1, other.points.end());
+ polyline.width.insert(polyline.width.end(), other.width.begin(), other.width.end());
+ polyline.endpoints.second = other.endpoints.second;
+ assert(polyline.width.size() == polyline.points.size()*2 - 2);
+
+ pp.erase(pp.begin() + j);
+ j = i; // restart search from i+1
+ }
+ }
+ }
+
+ polylines->insert(polylines->end(), pp.begin(), pp.end());
+}
+
+void ExPolygon::medial_axis(double min_width, double max_width, Polylines* polylines) const
+{
+ ThickPolylines tp;
+ this->medial_axis(min_width, max_width, &tp);
+ polylines->reserve(polylines->size() + tp.size());
+ for (auto &pl : tp)
+ polylines->emplace_back(pl.points);
+}
+
+Lines ExPolygon::lines() const
+{
+ Lines lines = this->contour.lines();
+ for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
+ Lines hole_lines = h->lines();
+ lines.insert(lines.end(), hole_lines.begin(), hole_lines.end());
+ }
+ return lines;
+}
+
+// Do expolygons match? If they match, they must have the same topology,
+// however their contours may be rotated.
+bool expolygons_match(const ExPolygon &l, const ExPolygon &r)
+{
+ if (l.holes.size() != r.holes.size() || ! polygons_match(l.contour, r.contour))
+ return false;
+ for (size_t hole_idx = 0; hole_idx < l.holes.size(); ++ hole_idx)
+ if (! polygons_match(l.holes[hole_idx], r.holes[hole_idx]))
+ return false;
+ return true;
+}
+
+BoundingBox get_extents(const ExPolygon &expolygon)
+{
+ return get_extents(expolygon.contour);
+}
+
+BoundingBox get_extents(const ExPolygons &expolygons)
+{
+ BoundingBox bbox;
+ if (! expolygons.empty()) {
+ for (size_t i = 0; i < expolygons.size(); ++ i)
+ if (! expolygons[i].contour.points.empty())
+ bbox.merge(get_extents(expolygons[i]));
+ }
+ return bbox;
+}
+
+BoundingBox get_extents_rotated(const ExPolygon &expolygon, double angle)
+{
+ return get_extents_rotated(expolygon.contour, angle);
+}
+
+BoundingBox get_extents_rotated(const ExPolygons &expolygons, double angle)
+{
+ BoundingBox bbox;
+ if (! expolygons.empty()) {
+ bbox = get_extents_rotated(expolygons.front().contour, angle);
+ for (size_t i = 1; i < expolygons.size(); ++ i)
+ bbox.merge(get_extents_rotated(expolygons[i].contour, angle));
+ }
+ return bbox;
+}
+
+extern std::vector get_extents_vector(const ExPolygons &polygons)
+{
+ std::vector out;
+ out.reserve(polygons.size());
+ for (ExPolygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it)
+ out.push_back(get_extents(*it));
+ return out;
+}
+
+bool has_duplicate_points(const ExPolygon &expoly)
+{
+#if 1
+ // Check globally.
+ size_t cnt = expoly.contour.points.size();
+ for (const Polygon &hole : expoly.holes)
+ cnt += hole.points.size();
+ Points allpts;
+ allpts.reserve(cnt);
+ allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
+ for (const Polygon &hole : expoly.holes)
+ allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
+ return has_duplicate_points(std::move(allpts));
+#else
+ // Check per contour.
+ if (has_duplicate_points(expoly.contour))
+ return true;
+ for (const Polygon &hole : expoly.holes)
+ if (has_duplicate_points(hole))
+ return true;
+ return false;
+#endif
+}
+
+bool has_duplicate_points(const ExPolygons &expolys)
+{
+#if 1
+ // Check globally.
+ Points allpts;
+ allpts.reserve(count_points(expolys));
+ for (const ExPolygon &expoly : expolys) {
+ allpts.insert(allpts.begin(), expoly.contour.points.begin(), expoly.contour.points.end());
+ for (const Polygon &hole : expoly.holes)
+ allpts.insert(allpts.end(), hole.points.begin(), hole.points.end());
+ }
+ return has_duplicate_points(std::move(allpts));
+#else
+ // Check per contour.
+ for (const ExPolygon &expoly : expolys)
+ if (has_duplicate_points(expoly))
+ return true;
+ return false;
+#endif
+}
+
+bool remove_same_neighbor(ExPolygons &expolygons)
+{
+ if (expolygons.empty())
+ return false;
+ bool remove_from_holes = false;
+ bool remove_from_contour = false;
+ for (ExPolygon &expoly : expolygons) {
+ remove_from_contour |= remove_same_neighbor(expoly.contour);
+ remove_from_holes |= remove_same_neighbor(expoly.holes);
+ }
+ // Removing of expolygons without contour
+ if (remove_from_contour)
+ expolygons.erase(std::remove_if(expolygons.begin(), expolygons.end(),
+ [](const ExPolygon &p) { return p.contour.points.size() <= 2; }),
+ expolygons.end());
+ return remove_from_holes || remove_from_contour;
+}
+
+bool remove_sticks(ExPolygon &poly)
+{
+ return remove_sticks(poly.contour) || remove_sticks(poly.holes);
+}
+
+bool remove_small_and_small_holes(ExPolygons &expolygons, double min_area)
+{
+ bool modified = false;
+ size_t free_idx = 0;
+ for (size_t expoly_idx = 0; expoly_idx < expolygons.size(); ++expoly_idx) {
+ if (std::abs(expolygons[expoly_idx].area()) >= min_area) {
+ // Expolygon is big enough, so also check all its holes
+ modified |= remove_small(expolygons[expoly_idx].holes, min_area);
+ if (free_idx < expoly_idx) {
+ std::swap(expolygons[expoly_idx].contour, expolygons[free_idx].contour);
+ std::swap(expolygons[expoly_idx].holes, expolygons[free_idx].holes);
+ }
+ ++free_idx;
+ } else
+ modified = true;
+ }
+ if (free_idx < expolygons.size())
+ expolygons.erase(expolygons.begin() + free_idx, expolygons.end());
+ return modified;
+}
+
+void keep_largest_contour_only(ExPolygons &polygons)
+{
+ if (polygons.size() > 1) {
+ double max_area = 0.;
+ ExPolygon* max_area_polygon = nullptr;
+ for (ExPolygon& p : polygons) {
+ double a = p.contour.area();
+ if (a > max_area) {
+ max_area = a;
+ max_area_polygon = &p;
+ }
+ }
+ assert(max_area_polygon != nullptr);
+ ExPolygon p(std::move(*max_area_polygon));
+ polygons.clear();
+ polygons.emplace_back(std::move(p));
+ }
+}
+
+} // namespace Slic3r
diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp
index 187ef27a11..87a1146d41 100644
--- a/src/libslic3r/ExPolygon.hpp
+++ b/src/libslic3r/ExPolygon.hpp
@@ -56,7 +56,7 @@ public:
bool on_boundary(const Point &point, double eps) const;
// Projection of a point onto the polygon.
Point point_projection(const Point &point) const;
-
+ void symmetric_y(const coord_t &y_axis);
// Does this expolygon overlap another expolygon?
// Either the ExPolygons intersect, or one is fully inside the other,
// and it is not inside a hole of the other expolygon.
diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp
index a3d8b45323..47726328c4 100644
--- a/src/libslic3r/Fill/Fill.cpp
+++ b/src/libslic3r/Fill/Fill.cpp
@@ -34,7 +34,6 @@ struct SurfaceFillParams
coordf_t overlap = 0.;
// Angle as provided by the region config, in radians.
float angle = 0.f;
- bool rotate_angle = true;
// 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.
@@ -68,6 +67,9 @@ struct SurfaceFillParams
// Params for lattice infill angles
float lattice_angle_1 = 0.f;
float lattice_angle_2 = 0.f;
+ float infill_lock_depth = 0;
+ float skin_infill_depth = 0;
+ bool symmetric_infill_y_axis = false;
// Params for 2D honeycomb
float infill_overhang_angle = 60.f;
@@ -85,7 +87,6 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(spacing);
RETURN_COMPARE_NON_EQUAL(overlap);
RETURN_COMPARE_NON_EQUAL(angle);
- RETURN_COMPARE_NON_EQUAL(rotate_angle);
RETURN_COMPARE_NON_EQUAL(density);
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
RETURN_COMPARE_NON_EQUAL(anchor_length);
@@ -100,33 +101,36 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(solid_infill_speed);
RETURN_COMPARE_NON_EQUAL(lattice_angle_1);
RETURN_COMPARE_NON_EQUAL(lattice_angle_2);
- RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
+ RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
+ RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
+ RETURN_COMPARE_NON_EQUAL(skin_infill_depth); RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
return false;
}
- bool operator==(const SurfaceFillParams &rhs) const {
- return this->extruder == rhs.extruder &&
- this->pattern == rhs.pattern &&
- this->spacing == rhs.spacing &&
- this->overlap == rhs.overlap &&
- this->angle == rhs.angle &&
- this->rotate_angle == rhs.rotate_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 &&
- this->anchor_length_max == rhs.anchor_length_max &&
- this->flow == rhs.flow &&
- this->extrusion_role == rhs.extrusion_role &&
- this->sparse_infill_speed == rhs.sparse_infill_speed &&
- this->top_surface_speed == rhs.top_surface_speed &&
- this->solid_infill_speed == rhs.solid_infill_speed &&
- this->lattice_angle_1 == rhs.lattice_angle_1 &&
- this->lattice_angle_2 == rhs.lattice_angle_2 &&
+ bool operator==(const SurfaceFillParams &rhs) const {
+ return this->extruder == rhs.extruder &&
+ 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 &&
+ this->anchor_length_max == rhs.anchor_length_max &&
+ this->flow == rhs.flow &&
+ this->extrusion_role == rhs.extrusion_role &&
+ this->sparse_infill_speed == rhs.sparse_infill_speed &&
+ this->top_surface_speed == rhs.top_surface_speed &&
+ this->solid_infill_speed == rhs.solid_infill_speed &&
+ this->lattice_angle_1 == rhs.lattice_angle_1 &&
+ this->lattice_angle_2 == rhs.lattice_angle_2 &&
+ this->infill_lock_depth == rhs.infill_lock_depth &&
+ this->skin_infill_depth == rhs.skin_infill_depth &&
this->infill_overhang_angle == rhs.infill_overhang_angle;
- }
+ }
};
struct SurfaceFill {
@@ -602,16 +606,34 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
#endif
}
-std::vector group_fills(const Layer &layer)
+std::vector group_fills(const Layer &layer, LockRegionParam &lock_param)
{
std::vector surface_fills;
-
// Fill in a map of a region & surface to SurfaceFillParams.
std::set set_surface_params;
std::vector> region_to_surface_params(layer.regions().size(), std::vector());
SurfaceFillParams params;
bool has_internal_voids = false;
const PrintObjectConfig& object_config = layer.object()->config();
+
+ auto append_flow_param = [](std::map &flow_params, Flow flow, const ExPolygon &exp) {
+ auto it = flow_params.find(flow);
+ if (it == flow_params.end())
+ flow_params.insert({flow, {exp}});
+ else
+ it->second.push_back(exp);
+ it++;
+ };
+
+ auto append_density_param = [](std::map &density_params, float density, const ExPolygon &exp) {
+ auto it = density_params.find(density);
+ if (it == density_params.end())
+ density_params.insert({density, {exp}});
+ else
+ it->second.push_back(exp);
+ it++;
+ };
+
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
const LayerRegion &layerm = *layer.regions()[region_id];
region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr);
@@ -628,8 +650,19 @@ std::vector group_fills(const Layer &layer)
params.lattice_angle_1 = region_config.lattice_angle_1;
params.lattice_angle_2 = region_config.lattice_angle_2;
params.infill_overhang_angle = region_config.infill_overhang_angle;
+ if (params.pattern == ipLockedZag) {
+ params.infill_lock_depth = scale_(region_config.infill_lock_depth);
+ params.skin_infill_depth = scale_(region_config.skin_infill_depth);
+ }
+ if (params.pattern == ipCrossZag || params.pattern == ipLockedZag) {
+ params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
+ } else if (params.pattern == ipZigZag) {
- if (surface.is_solid()) {
+
+ params.symmetric_infill_y_axis = region_config.symmetric_infill_y_axis;
+ }
+
+ if (surface.is_solid()) {
params.density = 100.f;
//FIXME for non-thick bridges, shall we allow a bottom surface pattern?
if (surface.is_solid_infill())
@@ -667,10 +700,8 @@ std::vector group_fills(const Layer &layer)
params.bridge_angle = float(surface.bridge_angle);
if (params.extrusion_role == erInternalInfill) {
params.angle = float(Geometry::deg2rad(region_config.infill_direction.value));
- params.rotate_angle = (params.pattern == ipRectilinear || params.pattern == ipLine);
} else {
params.angle = float(Geometry::deg2rad(region_config.solid_infill_direction.value));
- params.rotate_angle = region_config.rotate_solid_infill_direction;
}
// Calculate the actual flow we'll be using for this infill.
@@ -709,7 +740,28 @@ std::vector group_fills(const Layer &layer)
params.anchor_length = std::min(params.anchor_length, params.anchor_length_max);
}
- auto it_params = set_surface_params.find(params);
+ //get locked region param
+ if (params.pattern == ipLockedZag){
+ const PrintObject *object = layerm.layer()->object();
+ auto nozzle_diameter = float(object->print()->config().nozzle_diameter.get_at(layerm.region().extruder(extrusion_role) - 1));
+ Flow skin_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skin_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness));
+ //add skin flow
+ append_flow_param(lock_param.skin_flow_params, skin_flow, surface.expolygon);
+
+ Flow skeleton_flow = params.bridge ? params.flow : Flow::new_from_config_width(extrusion_role, region_config.skeleton_infill_line_width, nozzle_diameter, float((surface.thickness == -1) ? layer.height : surface.thickness)) ;
+ // add skeleton flow
+ append_flow_param(lock_param.skeleton_flow_params, skeleton_flow, surface.expolygon);
+
+ // add skin density
+ append_density_param(lock_param.skin_density_params, float(0.01 * region_config.skin_infill_density), surface.expolygon);
+
+ // add skin density
+ append_density_param(lock_param.skeleton_density_params, float(0.01 * region_config.skeleton_infill_density), surface.expolygon);
+
+ }
+
+ auto it_params = set_surface_params.find(params);
+
if (it_params == set_surface_params.end())
it_params = set_surface_params.insert(it_params, params);
region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()] = &(*it_params);
@@ -829,7 +881,6 @@ std::vector group_fills(const Layer &layer)
params.density = 100.f;
params.extrusion_role = erSolidInfill;
params.angle = float(Geometry::deg2rad(layerm.region().config().solid_infill_direction.value));
- params.rotate_angle = layerm.region().config().rotate_solid_infill_direction;
// calculate the actual flow we'll be using for this infill
params.flow = layerm.flow(frSolidInfill);
params.spacing = params.flow.spacing();
@@ -914,8 +965,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
// this->export_region_fill_surfaces_to_svg_debug("10_fill-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
-
- std::vector surface_fills = group_fills(*this);
+ LockRegionParam lock_param;
+ std::vector surface_fills = group_fills(*this, lock_param);
const Slic3r::BoundingBox bbox = this->object()->bounding_box();
const auto resolution = this->object()->print()->config().resolution.value;
@@ -933,14 +984,22 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
f->layer_id = this->id();
f->z = this->print_z;
f->angle = surface_fill.params.angle;
- f->rotate_angle = surface_fill.params.rotate_angle;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
f->print_config = &this->object()->print()->config();
f->print_object_config = &this->object()->config();
-
- if (surface_fill.params.pattern == ipLightning)
+ f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
+ if (surface_fill.params.pattern == ipConcentricInternal) {
+ FillConcentricInternal *fill_concentric = dynamic_cast(f.get());
+ assert(fill_concentric != nullptr);
+ fill_concentric->print_config = &this->object()->print()->config();
+ fill_concentric->print_object_config = &this->object()->config();
+ } else if (surface_fill.params.pattern == ipConcentric) {
+ FillConcentric *fill_concentric = dynamic_cast(f.get());
+ assert(fill_concentric != nullptr);
+ fill_concentric->print_config = &this->object()->print()->config();
+ fill_concentric->print_object_config = &this->object()->config();
+ } else if (surface_fill.params.pattern == ipLightning)
dynamic_cast(f.get())->generator = lightning_generator;
-
// calculate flow spacing for infill pattern generation
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
double link_max_length = 0.;
@@ -979,11 +1038,41 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.extrusion_role = surface_fill.params.extrusion_role;
params.using_internal_flow = using_internal_flow;
params.no_extrusion_overlap = surface_fill.params.overlap;
- params.config = &layerm->region().config();
+ auto ®ion_config = layerm->region().config();
+
+ ConfigOptionFloats rotate_angles;
+ rotate_angles.deserialize( surface_fill.params.extrusion_role == erInternalInfill ? region_config.sparse_infill_rotate_template.value : region_config.solid_infill_rotate_template.value);
+ auto rotate_angle_idx = f->layer_id % rotate_angles.size();
+ f->rotate_angle = Geometry::deg2rad(rotate_angles.values[rotate_angle_idx]);
+
+ params.config = ®ion_config;
+ params.pattern = surface_fill.params.pattern;
+ if( surface_fill.params.pattern == ipLockedZag ) {
+ params.locked_zag = true;
+ params.infill_lock_depth = surface_fill.params.infill_lock_depth;
+ params.skin_infill_depth = surface_fill.params.skin_infill_depth;
+ f->set_lock_region_param(lock_param);
+ }
+ if (surface_fill.params.pattern == ipCrossZag || surface_fill.params.pattern == ipLockedZag) {
+ if (f->layer_id % 2 == 0) {
+ params.horiz_move -= scale_(region_config.infill_shift_step) * (f->layer_id / 2);
+ } else {
+ params.horiz_move += scale_(region_config.infill_shift_step) * (f->layer_id / 2);
+ }
+
+ params.symmetric_infill_y_axis = surface_fill.params.symmetric_infill_y_axis;
+
+ }
if (surface_fill.params.pattern == ipGrid)
params.can_reverse = false;
for (ExPolygon& expoly : surface_fill.expolygons) {
- f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
+
+ f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
+ if (params.symmetric_infill_y_axis) {
+ params.symmetric_y_axis = f->extended_object_bounding_box().center().x();
+ expoly.symmetric_y(params.symmetric_y_axis);
+ }
+
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
f->spacing = surface_fill.params.spacing;
surface_fill.surface.expolygon = std::move(expoly);
@@ -1023,9 +1112,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) const
{
- std::vector surface_fills = group_fills(*this);
- const Slic3r::BoundingBox bbox = this->object()->bounding_box();
- const auto resolution = this->object()->print()->config().resolution.value;
+ LockRegionParam skin_inner_param;
+ std::vector surface_fills = group_fills(*this, skin_inner_param);
+ const Slic3r::BoundingBox bbox = this->object()->bounding_box();
+ const auto resolution = this->object()->print()->config().resolution.value;
Polylines sparse_infill_polylines{};
@@ -1059,7 +1149,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
case ipTpmsD:
case ipHilbertCurve:
case ipArchimedeanChords:
- case ipOctagramSpiral: break;
+ case ipOctagramSpiral:
+ case ipZigZag:
+ case ipCrossZag:
+ case ipLockedZag: break;
}
// Create the filler object.
diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/src/libslic3r/Fill/Fill3DHoneycomb.hpp
index f7a27cf4f9..ccb37a36a0 100644
--- a/src/libslic3r/Fill/Fill3DHoneycomb.hpp
+++ b/src/libslic3r/Fill/Fill3DHoneycomb.hpp
@@ -15,6 +15,10 @@ public:
Fill* clone() const override { return new Fill3DHoneycomb(*this); };
~Fill3DHoneycomb() override {}
+ // require bridge flow since most of this pattern hangs in air
+ bool use_bridge_flow() const override { return true; }
+ bool is_self_crossing() override { return false; }
+
protected:
void _fill_surface_single(
const FillParams ¶ms,
diff --git a/src/libslic3r/Fill/FillAdaptive.hpp b/src/libslic3r/Fill/FillAdaptive.hpp
index 0578cc3e1b..0132ffaa6c 100644
--- a/src/libslic3r/Fill/FillAdaptive.hpp
+++ b/src/libslic3r/Fill/FillAdaptive.hpp
@@ -71,6 +71,7 @@ protected:
// may not be optimal as the internal infill lines may get extruded before the long infill
// lines to which the short infill lines are supposed to anchor.
bool no_sort() const override { return false; }
+ bool is_self_crossing() override { return true; }
};
} // namespace FillAdaptive
diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp
index 3b2f5bcc5c..9f5a54a0d9 100644
--- a/src/libslic3r/Fill/FillBase.cpp
+++ b/src/libslic3r/Fill/FillBase.cpp
@@ -67,6 +67,9 @@ Fill* Fill::new_from_type(const InfillPattern type)
// BBS: for bottom and top surface only
// Orca: Replace BBS implementation with Prusa implementation
case ipMonotonicLine: return new FillMonotonicLines();
+ case ipZigZag: return new FillZigZag();
+ case ipCrossZag: return new FillCrossZag();
+ case ipLockedZag: return new FillLockedZag();
default: throw Slic3r::InvalidArgument("unknown type");
}
}
@@ -243,7 +246,7 @@ void Fill::_create_gap_fill(const Surface* surface, const FillParams& params, Ex
// Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends.
-// This function possibly increases the spacing, never decreases,
+// This function possibly increases the spacing, never decreases,
// and for a narrow width the increase in spacing may become severe,
// therefore the adjustment is limited to 20% increase.
coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
@@ -252,8 +255,8 @@ coord_t Fill::_adjust_solid_spacing(const coord_t width, const coord_t distance)
assert(distance > 0);
// floor(width / distance)
const auto number_of_intervals = coord_t((width - EPSILON) / distance);
- coord_t distance_new = (number_of_intervals == 0) ?
- distance :
+ coord_t distance_new = (number_of_intervals == 0) ?
+ distance :
coord_t((width - EPSILON) / number_of_intervals);
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
assert(factor > 1. - 1e-5);
@@ -279,8 +282,8 @@ std::pair Fill::_infill_direction(const Surface *surface) const
// Bounding box is the bounding box of a perl object Slic3r::Print::Object (c++ object Slic3r::PrintObject)
// The bounding box is only undefined in unit tests.
- Point out_shift = empty(this->bounding_box) ?
- surface->expolygon.contour.bounding_box().center() :
+ Point out_shift = empty(this->bounding_box) ?
+ surface->expolygon.contour.bounding_box().center() :
this->bounding_box.center();
#if 0
@@ -354,10 +357,10 @@ struct ContourIntersectionPoint {
bool could_take_next() const throw() { return ! this->consumed && this->contour_not_taken_length_next > SCALED_EPSILON; }
// Could extrude a complete segment from this to this->prev_on_contour.
- bool could_connect_prev() const throw()
+ bool could_connect_prev() const throw()
{ return ! this->consumed && this->prev_on_contour != this && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
// Could extrude a complete segment from this to this->next_on_contour.
- bool could_connect_next() const throw()
+ bool could_connect_next() const throw()
{ return ! this->consumed && this->next_on_contour != this && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
};
@@ -566,7 +569,7 @@ static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, Cont
}
static void take_limited(
- Polyline &pl1, const Points &contour, const std::vector ¶ms,
+ Polyline &pl1, const Points &contour, const std::vector ¶ms,
ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, double take_max_length, double line_half_width)
{
#ifndef NDEBUG
@@ -730,8 +733,8 @@ static inline SegmentPoint clip_end_segment_and_point(const Points &polyline, do
// Calculate intersection of a line with a thick segment.
// Returns Eucledian parameters of the line / thick segment overlap.
static inline bool line_rounded_thick_segment_collision(
- const Vec2d &line_a, const Vec2d &line_b,
- const Vec2d &segment_a, const Vec2d &segment_b, const double offset,
+ const Vec2d &line_a, const Vec2d &line_b,
+ const Vec2d &segment_a, const Vec2d &segment_b, const double offset,
std::pair &out_interval)
{
const Vec2d line_v0 = line_b - line_a;
@@ -794,8 +797,8 @@ static inline bool line_rounded_thick_segment_collision(
std::pair interval;
if (Geometry::liang_barsky_line_clipping_interval(
Vec2d(line_p0.dot(dir_x), line_p0.dot(dir_y)),
- Vec2d(line_v0.dot(dir_x), line_v0.dot(dir_y)),
- BoundingBoxf(Vec2d(0., - offset), Vec2d(segment_l, offset)),
+ Vec2d(line_v0.dot(dir_x), line_v0.dot(dir_y)),
+ BoundingBoxf(Vec2d(0., - offset), Vec2d(segment_l, offset)),
interval))
extend_interval(interval.first, interval.second);
} else
@@ -1155,7 +1158,7 @@ void mark_boundary_segments_touching_infill(
// Clip the infill polyline by the Eucledian distance along the polyline.
SegmentPoint start_point = clip_start_segment_and_point(polyline.points, clip_distance);
SegmentPoint end_point = clip_end_segment_and_point(polyline.points, clip_distance);
- if (start_point.valid() && end_point.valid() &&
+ if (start_point.valid() && end_point.valid() &&
(start_point.idx_segment < end_point.idx_segment || (start_point.idx_segment == end_point.idx_segment && start_point.t < end_point.t))) {
// The clipped polyline is non-empty.
#ifdef INFILL_DEBUG_OUTPUT
@@ -1295,21 +1298,21 @@ struct BoundaryInfillGraph
};
static Direction dir(const Point &p1, const Point &p2) {
- return p1.x() == p2.x() ?
+ 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() ?
+ 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() ?
+ return cp.could_take_next() ?
dir(this->point(cp), this->point(*cp.next_on_contour)) :
Taken;
}
@@ -1367,7 +1370,7 @@ static inline void mark_boundary_segments_overlapping_infill(
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,
+ // 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;
@@ -1399,7 +1402,7 @@ static inline void mark_boundary_segments_overlapping_infill(
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,
+ // 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;
@@ -1496,7 +1499,7 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
ContourIntersectionPoint *pthis = &out.map_infill_end_point_to_boundary[it->second];
if (pprev) {
pprev->next_on_contour = pthis;
- pthis->prev_on_contour = pprev;
+ pthis->prev_on_contour = pprev;
} else
pfirst = pthis;
contour_intersection_points.emplace_back(pthis);
@@ -1521,7 +1524,7 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
ip->param = contour_params[ip->point_idx];
// and measure distance to the previous and next intersection point.
const double contour_length = contour_params.back();
- for (ContourIntersectionPoint *ip : contour_intersection_points)
+ for (ContourIntersectionPoint *ip : contour_intersection_points)
if (ip->next_on_contour == ip) {
assert(ip->prev_on_contour == ip);
ip->contour_not_taken_length_prev = ip->contour_not_taken_length_next = contour_length;
@@ -1556,6 +1559,18 @@ BoundaryInfillGraph create_boundary_infill_graph(const Polylines &infill_ordered
return out;
}
+// The extended bounding box of the whole object that covers any rotation of every layer.
+BoundingBox Fill::extended_object_bounding_box() const
+{
+ BoundingBox out = bounding_box;
+ out.merge(Point(out.min.y(), out.min.x()));
+ out.merge(Point(out.max.y(), out.max.x()));
+
+ // The bounding box is scaled by sqrt(2.) to ensure that the bounding box
+ // covers any possible rotations.
+ return out.scaled(sqrt(2.));
+}
+
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());
@@ -1927,14 +1942,14 @@ static inline void base_support_extend_infill_lines(Polylines &infill, BoundaryI
// The contour is supposed to enter the "forbidden" zone outside of the (left, right) band at tbegin and also at tend.
static inline void emit_loops_in_band(
// Vertical band, which will trim the contour between tbegin and tend.
- coord_t left,
+ coord_t left,
coord_t right,
// Contour and its parametrization.
const Points &contour,
const std::vector &contour_params,
// Span of the parameters of an arch to trim with the vertical band.
double tbegin,
- double tend,
+ double tend,
// Minimum arch length to put into polylines_out. Shorter arches are not necessary to support a dense support infill.
double min_length,
Polylines &polylines_out)
@@ -1990,13 +2005,13 @@ static inline void emit_loops_in_band(
};
enum InOutBand {
- Entering,
+ Entering,
Leaving,
};
class State {
public:
- State(coord_t left, coord_t right, double min_length, Polylines &polylines_out) :
+ State(coord_t left, coord_t right, double min_length, Polylines &polylines_out) :
m_left(left), m_right(right), m_min_length(min_length), m_polylines_out(polylines_out) {}
void add_inner_point(const Point* p)
@@ -2297,7 +2312,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector SCALED_EPSILON &&
+ if (cp.contour_not_taken_length_prev > SCALED_EPSILON &&
(cost_prev.self_loop ?
cost_prev.cost > cap_cost :
cost_prev.cost > cost_veryhigh)) {
@@ -2643,7 +2658,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector SCALED_EPSILON &&
+ if (cp.contour_not_taken_length_next > SCALED_EPSILON &&
(cost_next.self_loop ?
cost_next.cost > cap_cost :
cost_next.cost > cost_veryhigh)) {
diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp
index 62eeb9000e..4348ba5127 100644
--- a/src/libslic3r/Fill/FillBase.hpp
+++ b/src/libslic3r/Fill/FillBase.hpp
@@ -36,6 +36,15 @@ public:
InfillFailedException() : Slic3r::RuntimeError("Infill failed") {}
};
+struct LockRegionParam
+{
+ LockRegionParam() {}
+ std::map skin_density_params;
+ std::map skeleton_density_params;
+ std::map skin_flow_params;
+ std::map skeleton_flow_params;
+};
+
struct FillParams
{
bool full_infill() const { return density > 0.9999f; }
@@ -72,6 +81,7 @@ struct FillParams
// For 2D lattice
coordf_t lattice_angle_1 { 0.f };
coordf_t lattice_angle_2 { 0.f };
+ InfillPattern pattern{ ipRectilinear };
// For 2D Honeycomb
float infill_overhang_angle { 60 };
@@ -85,6 +95,13 @@ struct FillParams
const PrintRegionConfig* config{ nullptr };
bool dont_sort{ false }; // do not sort the lines, just simply connect them
bool can_reverse{true};
+
+ float horiz_move{0.0}; //move infill to get cross zag pattern
+ bool symmetric_infill_y_axis{false};
+ coord_t symmetric_y_axis{0};
+ bool locked_zag{false};
+ float infill_lock_depth{0.0};
+ float skin_infill_depth{0.0};
};
static_assert(IsTriviallyCopyable::value, "FillParams class is not POD (and it should be - see constructor).");
@@ -102,7 +119,7 @@ public:
// in radians, ccw, 0 = East
float angle;
// Orca: enable angle shifting for layer change
- bool rotate_angle{ true };
+ float rotate_angle{ M_PI/180.0 };
// In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines.
// Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic.
// If left to zero, the links will not be limited.
@@ -135,20 +152,25 @@ public:
static bool use_bridge_flow(const InfillPattern type);
void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; }
-
+ BoundingBox extended_object_bounding_box() const;
// Use bridge flow for the fill?
virtual bool use_bridge_flow() const { return false; }
// Do not sort the fill lines to optimize the print head path?
virtual bool no_sort() const { return false; }
+ virtual bool is_self_crossing() = 0;
+
+ // Return true if infill has a consistent pattern between layers.
+ virtual bool has_consistent_pattern() const { return false; }
+
// Perform the fill.
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
virtual ThickPolylines fill_surface_arachne(const Surface* surface, const FillParams& params);
-
+ virtual void set_lock_region_param(const LockRegionParam &lock_param){};
// BBS: this method is used to fill the ExtrusionEntityCollection.
// It call fill_surface by default
- virtual void fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out);
+ virtual void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out);
protected:
Fill() :
@@ -159,7 +181,7 @@ protected:
overlap(0.),
// Initial angle is undefined.
angle(FLT_MAX),
- rotate_angle(true),
+ rotate_angle(M_PI/180.0),
link_max_length(0),
loop_clipping(0),
// The initial bounding box is empty, therefore undefined.
@@ -168,11 +190,11 @@ protected:
// The expolygon may be modified by the method to avoid a copy.
virtual void _fill_surface_single(
- const FillParams & /* params */,
+ const FillParams & /* params */,
unsigned int /* thickness_layers */,
- const std::pair & /* direction */,
+ const std::pair & /* direction */,
ExPolygon /* expolygon */,
- Polylines & /* polylines_out */) {};
+ Polylines & /* polylines_out */) {}
// Used for concentric infill to generate ThickPolylines using Arachne.
virtual void _fill_surface_single(const FillParams& params,
@@ -181,7 +203,7 @@ protected:
ExPolygon expolygon,
ThickPolylines& thick_polylines_out) {}
- virtual float _layer_angle(size_t idx) const { return (rotate_angle && (idx & 1)) ? float(M_PI/2.) : 0; }
+ virtual float _layer_angle(size_t idx) const { return rotate_angle; }
virtual std::pair _infill_direction(const Surface *surface) const;
diff --git a/src/libslic3r/Fill/FillConcentric.hpp b/src/libslic3r/Fill/FillConcentric.hpp
index 476c9b8757..56040d848d 100644
--- a/src/libslic3r/Fill/FillConcentric.hpp
+++ b/src/libslic3r/Fill/FillConcentric.hpp
@@ -9,6 +9,7 @@ class FillConcentric : public Fill
{
public:
~FillConcentric() override = default;
+ bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillConcentric(*this); };
diff --git a/src/libslic3r/Fill/FillConcentricInternal.hpp b/src/libslic3r/Fill/FillConcentricInternal.hpp
index e362d72b81..2c93eb5ee9 100644
--- a/src/libslic3r/Fill/FillConcentricInternal.hpp
+++ b/src/libslic3r/Fill/FillConcentricInternal.hpp
@@ -10,6 +10,7 @@ class FillConcentricInternal : public Fill
public:
~FillConcentricInternal() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override;
+ bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillConcentricInternal(*this); };
diff --git a/src/libslic3r/Fill/FillCrossHatch.hpp b/src/libslic3r/Fill/FillCrossHatch.hpp
index 859d5bd8b5..34c46116eb 100644
--- a/src/libslic3r/Fill/FillCrossHatch.hpp
+++ b/src/libslic3r/Fill/FillCrossHatch.hpp
@@ -14,6 +14,7 @@ class FillCrossHatch : public Fill
public:
Fill *clone() const override { return new FillCrossHatch(*this); };
~FillCrossHatch() override {}
+ bool is_self_crossing() override { return false; }
protected:
void _fill_surface_single(
diff --git a/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp
index ac66dfca98..751a7c2570 100644
--- a/src/libslic3r/Fill/FillGyroid.hpp
+++ b/src/libslic3r/Fill/FillGyroid.hpp
@@ -15,6 +15,7 @@ public:
// require bridge flow since most of this pattern hangs in air
bool use_bridge_flow() const override { return false; }
+ bool is_self_crossing() override { return false; }
// Correction applied to regular infill angle to maximize printing
// speed in default configuration (degrees)
diff --git a/src/libslic3r/Fill/FillHoneycomb.hpp b/src/libslic3r/Fill/FillHoneycomb.hpp
index 707e976fd9..55be423854 100644
--- a/src/libslic3r/Fill/FillHoneycomb.hpp
+++ b/src/libslic3r/Fill/FillHoneycomb.hpp
@@ -13,6 +13,7 @@ class FillHoneycomb : public Fill
{
public:
~FillHoneycomb() override {}
+ bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillHoneycomb(*this); };
diff --git a/src/libslic3r/Fill/FillLightning.hpp b/src/libslic3r/Fill/FillLightning.hpp
index 6e672783a1..74aa159b0b 100644
--- a/src/libslic3r/Fill/FillLightning.hpp
+++ b/src/libslic3r/Fill/FillLightning.hpp
@@ -20,6 +20,7 @@ class Filler : public Slic3r::Fill
{
public:
~Filler() override = default;
+ bool is_self_crossing() override { return false; }
Generator *generator { nullptr };
protected:
diff --git a/src/libslic3r/Fill/FillLine.hpp b/src/libslic3r/Fill/FillLine.hpp
index 9bf2b97e09..4ad9ca49a4 100644
--- a/src/libslic3r/Fill/FillLine.hpp
+++ b/src/libslic3r/Fill/FillLine.hpp
@@ -14,6 +14,7 @@ class FillLine : public Fill
public:
Fill* clone() const override { return new FillLine(*this); };
~FillLine() override = default;
+ bool is_self_crossing() override { return false; }
protected:
void _fill_surface_single(
diff --git a/src/libslic3r/Fill/FillPlanePath.hpp b/src/libslic3r/Fill/FillPlanePath.hpp
index f05f3209aa..1371e8506a 100644
--- a/src/libslic3r/Fill/FillPlanePath.hpp
+++ b/src/libslic3r/Fill/FillPlanePath.hpp
@@ -37,6 +37,7 @@ class FillPlanePath : public Fill
{
public:
~FillPlanePath() override = default;
+ bool is_self_crossing() override { return false; }
protected:
void _fill_surface_single(
diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp
index 6948cfd799..261ae3c046 100644
--- a/src/libslic3r/Fill/FillRectilinear.cpp
+++ b/src/libslic3r/Fill/FillRectilinear.cpp
@@ -987,7 +987,6 @@ static std::vector slice_region_by_vertical_lines(con
throw;
}
#endif //INFILL_DEBUG_OUTPUT
-
return segs;
}
@@ -1352,8 +1351,11 @@ static SegmentIntersection& end_of_vertical_run(SegmentedIntersectionLine &il, S
return const_cast(end_of_vertical_run(std::as_const(il), std::as_const(start)));
}
-static void traverse_graph_generate_polylines(
- const ExPolygonWithOffset& poly_with_offset, const FillParams& params, const coord_t link_max_length, std::vector& segs, Polylines& polylines_out)
+static void traverse_graph_generate_polylines(const ExPolygonWithOffset &poly_with_offset,
+ const FillParams ¶ms,
+ std::vector &segs,
+ const bool consistent_pattern,
+ Polylines &polylines_out)
{
// For each outer only chords, measure their maximum distance to the bow of the outer contour.
// Mark an outer only chord as consumed, if the distance is low.
@@ -1387,34 +1389,28 @@ static void traverse_graph_generate_polylines(
pointLast = polylines_out.back().points.back();
for (;;) {
if (i_intersection == -1) {
- // The path has been interrupted. Find a next starting point, closest to the previous extruder position.
- coordf_t dist2min = std::numeric_limits().max();
- for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++ i_vline2) {
+ // The path has been interrupted. Find a next starting point.
+ for (int i_vline2 = 0; i_vline2 < int(segs.size()); ++i_vline2) {
const SegmentedIntersectionLine &vline = segs[i_vline2];
- if (! vline.intersections.empty()) {
+ if (!vline.intersections.empty()) {
assert(vline.intersections.size() > 1);
// Even number of intersections with the loops.
assert((vline.intersections.size() & 1) == 0);
assert(vline.intersections.front().type == SegmentIntersection::OUTER_LOW);
- for (int i = 0; i < int(vline.intersections.size()); ++ i) {
- const SegmentIntersection& intrsctn = vline.intersections[i];
+
+ // For infill that needs to be consistent between layers (like Zig Zag),
+ // we are switching between forward and backward passes based on the line index.
+ const bool forward_pass = !consistent_pattern || (i_vline2 % 2 == 0);
+ for (int i = 0; i < int(vline.intersections.size()); ++i) {
+ const int intrsctn_idx = forward_pass ? i : int(vline.intersections.size()) - i - 1;
+ const SegmentIntersection &intrsctn = vline.intersections[intrsctn_idx];
if (intrsctn.is_outer()) {
- assert(intrsctn.is_low() || i > 0);
- bool consumed = intrsctn.is_low() ?
- intrsctn.consumed_vertical_up :
- vline.intersections[i - 1].consumed_vertical_up;
- if (! consumed) {
- coordf_t dist2 = sqr(coordf_t(pointLast(0) - vline.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
- if (dist2 < dist2min) {
- dist2min = dist2;
- i_vline = i_vline2;
- i_intersection = i;
- //FIXME We are taking the first left point always. Verify, that the caller chains the paths
- // by a shortest distance, while reversing the paths if needed.
- //if (polylines_out.empty())
- // Initial state, take the first line, which is the first from the left.
- goto found;
- }
+ assert(intrsctn.is_low() || intrsctn_idx > 0);
+ const bool consumed = intrsctn.is_low() ? intrsctn.consumed_vertical_up : vline.intersections[intrsctn_idx - 1].consumed_vertical_up;
+ if (!consumed) {
+ i_vline = i_vline2;
+ i_intersection = intrsctn_idx;
+ goto found;
}
}
}
@@ -1487,9 +1483,13 @@ static void traverse_graph_generate_polylines(
// 1) Find possible connection points on the previous / next vertical line.
int i_prev = it->left_horizontal();
int i_next = it->right_horizontal();
- bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection);
+
+ // To ensure pattern consistency between layers for Zig Zag infill, we always
+ // try to connect to the next vertical line and never to the previous vertical line.
+ bool intersection_prev_valid = intersection_on_prev_vertical_line_valid(segs, i_vline, i_intersection) && !consistent_pattern;
bool intersection_next_valid = intersection_on_next_vertical_line_valid(segs, i_vline, i_intersection);
bool intersection_horizontal_valid = intersection_prev_valid || intersection_next_valid;
+
// Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed.
if (i_prev != -1)
segs[i_vline - 1].intersections[i_prev].consumed_perimeter_right = true;
@@ -2737,6 +2737,17 @@ static void polylines_from_paths(const std::vector &path, c
}
}
+// The extended bounding box of the whole object that covers any rotation of every layer.
+BoundingBox FillRectilinear::extended_object_bounding_box() const {
+ BoundingBox out = this->bounding_box;
+ out.merge(Point(out.min.y(), out.min.x()));
+ out.merge(Point(out.max.y(), out.max.x()));
+
+ // The bounding box is scaled by sqrt(2.) to ensure that the bounding box
+ // covers any possible rotations.
+ return out.scaled(sqrt(2.));
+}
+
bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out)
{
// At the end, only the new polylines will be rotated back.
@@ -2749,6 +2760,8 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
// Rotate polygons so that we can work with vertical lines here
std::pair rotate_vector = this->_infill_direction(surface);
+ if (params.locked_zag)
+ rotate_vector.first += float(M_PI/2.);
rotate_vector.first += angleBase;
assert(params.density > 0.0001f && params.density <= 1.f);
@@ -2766,11 +2779,14 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
return true;
}
- BoundingBox bounding_box = poly_with_offset.bounding_box_src();
+ // For infill that needs to be consistent between layers (like Zig Zag),
+ // we use bounding box of whole object to match vertical lines between layers.
+ BoundingBox bounding_box_src = poly_with_offset.bounding_box_src();
+ BoundingBox bounding_box = this->has_consistent_pattern() ? this->extended_object_bounding_box() : bounding_box_src;
// define flow spacing according to requested density
if (params.full_infill() && !params.dont_adjust) {
- line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
+ line_spacing = this->_adjust_solid_spacing(bounding_box_src.size().x(), line_spacing);
this->spacing = unscale(line_spacing);
} else {
// extend bounding box so that our pattern will be aligned with other layers
@@ -2792,6 +2808,13 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
if (params.full_infill())
x0 += (line_spacing + coord_t(SCALED_EPSILON)) / 2;
+ int gap_line = params.horiz_move / line_spacing;
+ if (gap_line % 2 == 0) {
+ x0 += params.horiz_move - gap_line * line_spacing;
+ } else {
+ x0 += params.horiz_move - (gap_line - 1) * line_spacing;
+ n_vlines += 1;
+ }
#ifdef SLIC3R_DEBUG
static int iRun = 0;
BoundingBox bbox_svg = poly_with_offset.bounding_box_outer();
@@ -2803,7 +2826,6 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
}
iRun ++;
#endif /* SLIC3R_DEBUG */
-
std::vector segs = slice_region_by_vertical_lines(poly_with_offset, n_vlines, x0, line_spacing);
// Connect by horizontal / vertical links, classify the links based on link_max_length as too long.
connect_segment_intersections_by_contours(poly_with_offset, segs, params, link_max_length);
@@ -2848,8 +2870,9 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
std::vector path = chain_monotonic_regions(regions, poly_with_offset, segs, rng);
polylines_from_paths(path, poly_with_offset, segs, polylines_out);
}
- } else
- traverse_graph_generate_polylines(poly_with_offset, params, this->link_max_length, segs, polylines_out);
+ } else {
+ traverse_graph_generate_polylines(poly_with_offset, params, segs, this->has_consistent_pattern(), polylines_out);
+ }
#ifdef SLIC3R_DEBUG
{
@@ -2876,6 +2899,11 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
//FIXME rather simplify the paths to avoid very short edges?
//assert(! it->has_duplicate_points());
it->remove_duplicate_points();
+
+ //get origin direction infill
+ if (params.symmetric_infill_y_axis) {
+ it->symmetric_y(params.symmetric_y_axis);
+ }
}
#ifdef SLIC3R_DEBUG
@@ -2884,6 +2912,8 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
assert(! polyline.has_duplicate_points());
#endif /* SLIC3R_DEBUG */
+
+
return true;
}
@@ -2963,7 +2993,8 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar
Polylines FillRectilinear::fill_surface(const Surface *surface, const FillParams ¶ms)
{
Polylines polylines_out;
- if (params.full_infill()) {
+ // Orca Todo: fow now don't use fill_surface_by_multilines for zipzag infill
+ if (params.full_infill() || params.pattern == ipCrossZag || params.pattern == ipZigZag || params.pattern == ipLockedZag) {
if (!fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out))
BOOST_LOG_TRIVIAL(error) << "FillRectilinear::fill_surface() fill_surface_by_lines() failed to fill a region.";
} else {
@@ -3409,5 +3440,119 @@ void FillMonotonicLineWGapFill::fill_surface_by_lines(const Surface* surface, co
}
}*/
+void FillLockedZag::fill_surface_locked_zag (const Surface * surface,
+ const FillParams & params,
+ std::vector> &multi_width_polyline)
+{
+ // merge different part exps
+ // diff skin flow
+ Polylines skin_lines;
+ Polylines skeloton_lines;
+ double offset_threshold = params.skin_infill_depth;
+ double overlap_threshold = params.infill_lock_depth;
+ Surface cross_surface = *surface;
+ Surface zig_surface = *surface;
+ // inner exps
+ // inner union exps
+ ExPolygons zig_expas = offset_ex({surface->expolygon}, -offset_threshold);
+ ExPolygons cross_expas = diff_ex(surface->expolygon, zig_expas);
+
+ bool zig_get = false;
+ FillParams zig_params = params;
+ zig_params.horiz_move = 0;
+ // generate skeleton for diff density
+ auto generate_for_different_flow = [&multi_width_polyline](const std::map &flow_params, const Polylines &polylines) {
+ auto it = flow_params.begin();
+ while (it != flow_params.end()) {
+ ExPolygons region_exp = union_safety_offset_ex(it->second);
+
+ Polylines polys = intersection_pl(polylines, region_exp);
+ multi_width_polyline.emplace_back(polys, it->first);
+ it++;
+ }
+ };
+
+ auto it = this->lock_param.skeleton_density_params.begin();
+ while (it != this->lock_param.skeleton_density_params.end()) {
+ ExPolygons region_exp = union_safety_offset_ex(it->second);
+ ExPolygons exps = intersection_ex(region_exp, zig_expas);
+ zig_params.density = it->first;
+ exps = intersection_ex(offset_ex(exps, overlap_threshold), surface->expolygon);
+ for (ExPolygon &exp : exps) {
+ zig_surface.expolygon = exp;
+
+ Polylines zig_polylines_out = this->fill_surface(&zig_surface, zig_params);
+ skeloton_lines.insert(skeloton_lines.end(), zig_polylines_out.begin(), zig_polylines_out.end());
+ }
+ it++;
+ }
+
+ // set skeleton flow
+ generate_for_different_flow(this->lock_param.skeleton_flow_params, skeloton_lines);
+
+ // skin exps
+ bool cross_get = false;
+ FillParams cross_params = params;
+ cross_params.locked_zag = false;
+ auto skin_density = this->lock_param.skin_density_params.begin();
+ while (skin_density != this->lock_param.skin_density_params.end()) {
+ ExPolygons region_exp = union_safety_offset_ex(skin_density->second);
+ ExPolygons exps = intersection_ex(region_exp, cross_expas);
+ cross_params.density = skin_density->first;
+ for (ExPolygon &exp : exps) {
+ cross_surface.expolygon = exp;
+ Polylines cross_polylines_out = this->fill_surface(&cross_surface, cross_params);
+ skin_lines.insert(skin_lines.end(), cross_polylines_out.begin(), cross_polylines_out.end());
+ }
+ skin_density++;
+ }
+
+ generate_for_different_flow(this->lock_param.skin_flow_params, skin_lines);
+}
+
+void FillLockedZag::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out)
+{
+ Polylines polylines;
+ ThickPolylines thick_polylines;
+ std::vector> multi_width_polyline;
+ try {
+ this->fill_surface_locked_zag(surface, params, multi_width_polyline);
+ }
+ catch (InfillFailedException&) {}
+
+ if (!thick_polylines.empty() || !multi_width_polyline.empty()) {
+ // Save into layer.
+ ExtrusionEntityCollection* eec = nullptr;
+ out.push_back(eec = new ExtrusionEntityCollection());
+ // Only concentric fills are not sorted.
+ eec->no_sort = this->no_sort();
+ size_t idx = eec->entities.size();
+ {
+ for (std::pair &poly_with_flow: multi_width_polyline) {
+ // calculate actual flow from spacing (which might have been adjusted by the infill
+ // pattern generator)
+ double flow_mm3_per_mm = poly_with_flow.second.mm3_per_mm();
+ double flow_width = poly_with_flow.second.width();
+ if (params.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 = poly_with_flow.second.with_spacing(this->spacing);
+ flow_mm3_per_mm = new_flow.mm3_per_mm();
+ flow_width = new_flow.width();
+ }
+ extrusion_entities_append_paths(
+ eec->entities, std::move(poly_with_flow.first),
+ params.extrusion_role,
+ flow_mm3_per_mm, float(flow_width), poly_with_flow.second.height());
+ }
+ }
+ if (!params.can_reverse) {
+ for (size_t i = idx; i < eec->entities.size(); i++)
+ eec->entities[i]->set_reverse();
+ }
+ }
+}
} // namespace Slic3r
diff --git a/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp
index 2dbcad3e90..28b20ef1f2 100644
--- a/src/libslic3r/Fill/FillRectilinear.hpp
+++ b/src/libslic3r/Fill/FillRectilinear.hpp
@@ -16,6 +16,7 @@ public:
Fill* clone() const override { return new FillRectilinear(*this); }
~FillRectilinear() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return false; }
protected:
// Fill by single directional lines, interconnect the lines along perimeters.
@@ -28,6 +29,9 @@ protected:
float pattern_shift;
};
bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list &sweep_params, Polylines &polylines_out);
+
+ // The extended bounding box of the whole object that covers any rotation of every layer.
+ BoundingBox extended_object_bounding_box() const;
};
class FillAlignedRectilinear : public FillRectilinear
@@ -65,6 +69,7 @@ public:
Fill* clone() const override { return new FillGrid(*this); }
~FillGrid() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return true; }
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
@@ -89,6 +94,7 @@ public:
Fill* clone() const override { return new FillTriangles(*this); }
~FillTriangles() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return true; }
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
@@ -101,6 +107,7 @@ public:
Fill* clone() const override { return new FillStars(*this); }
~FillStars() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return true; }
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
@@ -113,6 +120,7 @@ public:
Fill* clone() const override { return new FillCubic(*this); }
~FillCubic() override = default;
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
+ bool is_self_crossing() override { return true; }
protected:
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
@@ -170,6 +178,7 @@ public:
public:
~FillMonotonicLineWGapFill() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override;
+ bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillMonotonicLineWGapFill(*this); };
@@ -179,9 +188,43 @@ private:
void fill_surface_by_lines(const Surface* surface, const FillParams& params, Polylines& polylines_out);
};*/
-Points sample_grid_pattern(const ExPolygon& expolygon, coord_t spacing, const BoundingBox& global_bounding_box);
-Points sample_grid_pattern(const ExPolygons& expolygons, coord_t spacing, const BoundingBox& global_bounding_box);
-Points sample_grid_pattern(const Polygons& polygons, coord_t spacing, const BoundingBox& global_bounding_box);
+class FillZigZag : public FillRectilinear
+{
+public:
+ Fill* clone() const override { return new FillZigZag(*this); }
+ ~FillZigZag() override = default;
+
+ bool has_consistent_pattern() const override { return true; }
+};
+
+class FillCrossZag : public FillRectilinear
+{
+public:
+ Fill *clone() const override { return new FillCrossZag(*this); }
+ ~FillCrossZag() override = default;
+
+ bool has_consistent_pattern() const override { return true; }
+};
+
+class FillLockedZag : public FillRectilinear
+{
+public:
+ Fill *clone() const override { return new FillLockedZag(*this); }
+ ~FillLockedZag() override = default;
+ LockRegionParam lock_param;
+
+ void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) override;
+
+ bool has_consistent_pattern() const override { return true; }
+ void set_lock_region_param(const LockRegionParam &lock_param) override { this->lock_param = lock_param;};
+ void fill_surface_locked_zag(const Surface * surface,
+ const FillParams & params,
+ std::vector> &multi_width_polyline);
+};
+
+Points sample_grid_pattern(const ExPolygon &expolygon, coord_t spacing, const BoundingBox &global_bounding_box);
+Points sample_grid_pattern(const ExPolygons &expolygons, coord_t spacing, const BoundingBox &global_bounding_box);
+Points sample_grid_pattern(const Polygons &polygons, coord_t spacing, const BoundingBox &global_bounding_box);
} // namespace Slic3r
diff --git a/src/libslic3r/Fill/FillTpmsD.hpp b/src/libslic3r/Fill/FillTpmsD.hpp
index 73b55e1e1c..5e8accc19c 100644
--- a/src/libslic3r/Fill/FillTpmsD.hpp
+++ b/src/libslic3r/Fill/FillTpmsD.hpp
@@ -25,20 +25,20 @@ public:
// speed in default configuration (degrees)
static constexpr float CorrectionAngle = -45.;
+ void _fill_surface_single(const FillParams& params,
+ unsigned int thickness_layers,
+ const std::pair& direction,
+ ExPolygon expolygon,
+ Polylines& polylines_out) override;
+
+ bool is_self_crossing() override { return false; }
+
// Density adjustment to have a good %of weight.
static constexpr double DensityAdjust = 2.1;
// Gyroid upper resolution tolerance (mm^-2)
static constexpr double PatternTolerance = 0.1;
-
-protected:
- void _fill_surface_single(
- const FillParams ¶ms,
- unsigned int thickness_layers,
- const std::pair &direction,
- ExPolygon expolygon,
- Polylines &polylines_out) override;
};
} // namespace Slic3r
diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp
index c7954158f5..79cb1b324d 100644
--- a/src/libslic3r/Flow.hpp
+++ b/src/libslic3r/Flow.hpp
@@ -82,6 +82,13 @@ public:
bool operator==(const Flow &rhs) const { return m_width == rhs.m_width && m_height == rhs.m_height && m_nozzle_diameter == rhs.m_nozzle_diameter && m_bridge == rhs.m_bridge; }
+ bool operator!=(const Flow &rhs) const{
+ return m_width != rhs.m_width || m_height != rhs.m_height || m_nozzle_diameter != rhs.m_nozzle_diameter || m_bridge != rhs.m_bridge;
+ }
+
+ bool operator <(const Flow &rhs) const {
+ return this->mm3_per_mm() < rhs.mm3_per_mm();
+ }
Flow with_width (float width) const {
assert(! m_bridge);
return Flow(width, m_height, rounded_rectangle_extrusion_spacing(width, m_height), m_nozzle_diameter, m_bridge);
diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp
index f027e93a07..fbf898234e 100644
--- a/src/libslic3r/MultiPoint.cpp
+++ b/src/libslic3r/MultiPoint.cpp
@@ -507,4 +507,11 @@ BoundingBox get_extents_rotated(const MultiPoint &mp, double angle)
return get_extents_rotated(mp.points, angle);
}
+void MultiPoint::symmetric_y(const coord_t &x_axis)
+{
+ for (Point &pt : points) {
+ pt(0) = 2 * x_axis - pt(0);
+ }
+}
+
}
diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp
index db51a6a87a..0dfb98f4b4 100644
--- a/src/libslic3r/MultiPoint.hpp
+++ b/src/libslic3r/MultiPoint.hpp
@@ -94,7 +94,7 @@ public:
bool intersection(const Line& line, Point* intersection) const;
bool first_intersection(const Line& line, Point* intersection) const;
bool intersections(const Line &line, Points *intersections) const;
-
+ void symmetric_y(const coord_t &y_axis);
static Points _douglas_peucker(const Points &points, const double tolerance);
static Points visivalingam(const Points& pts, const double tolerance);
static Points concave_hull_2d(const Points& pts, const double tolerence);
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index b640815ec7..3812601bb5 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -786,7 +786,7 @@ static std::vector s_Preset_print_options {
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction",
"seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "lattice_angle_1", "lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern",
- "infill_direction", "solid_infill_direction", "rotate_solid_infill_direction", "counterbore_hole_bridging",
+ "infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density",
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target",
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_inset",
"support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing",
@@ -806,8 +806,9 @@ static std::vector s_Preset_print_options {
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges","enable_extra_bridge_layer", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
"filename_format", "wall_filament", "support_bottom_z_distance",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
- "ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width",
- "inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
+ "ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width", "inner_wall_line_width",
+ "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
+ "skin_infill_line_width","skeleton_infill_line_width",
"top_surface_line_width", "support_line_width", "infill_wall_overlap","top_bottom_infill_wall_overlap", "bridge_flow", "internal_bridge_flow",
"elefant_foot_compensation", "elefant_foot_compensation_layers", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower",
"prime_tower_width", "prime_tower_brim_width", "prime_volume",
diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 1bb7834927..ae2b420049 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1387,7 +1387,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
if (!validate_extrusion_width(object->config(), "support_line_width", layer_height, err_msg))
return {err_msg, object, "support_line_width"};
}
- for (const char *opt_key : { "inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", "top_surface_line_width" })
+ for (const char *opt_key : { "inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", "top_surface_line_width","skin_infill_line_width" ,"skeleton_infill_line_width"})
for (const PrintRegion ®ion : object->all_regions())
if (!validate_extrusion_width(region.config(), opt_key, layer_height, err_msg))
return {err_msg, object, opt_key};
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index f0b82cbe36..d3eb2174e0 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -161,6 +161,9 @@ static t_config_enum_values s_keys_map_InfillPattern {
{ "lightning", ipLightning },
{ "crosshatch", ipCrossHatch},
{ "quartercubic", ipQuarterCubic},
+ { "zigzag", ipZigZag },
+ { "crosszag", ipCrossZag },
+ { "lockedzag", ipLockedZag }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
@@ -2348,13 +2351,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(45));
- def = this->add("rotate_solid_infill_direction", coBool);
- def->label = L("Rotate solid infill direction");
- def->category = L("Strength");
- def->tooltip = L("Rotate the solid infill direction by 90° for each layer.");
- def->mode = comAdvanced;
- def->set_default_value(new ConfigOptionBool(true));
-
def = this->add("sparse_infill_density", coPercent);
def->label = L("Sparse infill density");
def->category = L("Strength");
@@ -2392,6 +2388,9 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("lightning");
def->enum_values.push_back("crosshatch");
def->enum_values.push_back("quartercubic");
+ def->enum_values.push_back("zigzag");
+ def->enum_values.push_back("crosszag");
+ def->enum_values.push_back("lockedzag");
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Grid"));
@@ -2414,6 +2413,9 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Lightning"));
def->enum_labels.push_back(L("Cross Hatch"));
def->enum_labels.push_back(L("Quarter Cubic"));
+ def->enum_labels.push_back(L("Zig Zag"));
+ def->enum_labels.push_back(L("Cross Zag"));
+ def->enum_labels.push_back(L("Locked Zag"));
def->set_default_value(new ConfigOptionEnum(ipCrossHatch));
def = this->add("lattice_angle_1", coFloat);
@@ -3100,6 +3102,113 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
+ def = this->add("infill_shift_step", coFloat);
+ def->label = L("Infill shift step");
+ def->category = L("Strength");
+ def->tooltip = L("This parameter adds a slight displacement to each layer of infill to create a cross texture.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->max = 10;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloat(0.4));
+
+ //Orca
+ def = this->add("sparse_infill_rotate_template", coString);
+ def->label = L("Sparse infill rotatation template");
+ def->category = L("Strength");
+ def->tooltip = L("This parameter adds a rotation of sparse infill direction to each layer according to the specified template. "
+ "The template is a comma-separated list of angles in degrees, e.g. '0,90'. "
+ "The first angle is applied to the first layer, the second angle to the second layer, and so on. "
+ "If there are more layers than angles, the angles will be repeated. Note that not all all sparse infill patterns support rotation.");
+ def->sidetext = L("°");
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionString("0,90"));
+
+ //Orca
+ def = this->add("solid_infill_rotate_template", coString);
+ def->label = L("Solid infill rotatation template");
+ def->category = L("Strength");
+ def->tooltip = L("This parameter adds a rotation of solid infill direction to each layer according to the specified template. "
+ "The template is a comma-separated list of angles in degrees, e.g. '0,90'. "
+ "The first angle is applied to the first layer, the second angle to the second layer, and so on. "
+ "If there are more layers than angles, the angles will be repeated. Note that not all all solid infill patterns support rotation.");
+ def->sidetext = L("°");
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionString("0,90"));
+
+
+ def = this->add("skeleton_infill_density", coPercent);
+ def->label = L("Skeleton infill density");
+ def->category = L("Strength");
+ def->tooltip = L("The remaining part of the model contour after removing a certain depth from the surface is called the skeleton. This parameter is used to adjust the density of this section."
+ "When two regions have the same sparse infill settings but different skeleton densities, their skeleton areas will develop overlapping sections."
+ "default is as same as infill density.");
+ def->sidetext = "%";
+ def->min = 0;
+ def->max = 100;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionPercent(25));
+
+ def = this->add("skin_infill_density", coPercent);
+ def->label = L("Skin infill density");
+ def->category = L("Strength");
+ def->tooltip = L("The portion of the model's outer surface within a certain depth range is called the skin. This parameter is used to adjust the density of this section."
+ "When two regions have the same sparse infill settings but different skin densities, This area will not be split into two separate regions."
+ "default is as same as infill density.");
+ def->sidetext = "%";
+ def->min = 0;
+ def->max = 100;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionPercent(25));
+
+ def = this->add("skin_infill_depth", coFloat);
+ def->label = L("Skin infill depth");
+ def->category = L("Strength");
+ def->tooltip = L("The parameter sets the depth of skin.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->max = 100;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloat(2.0));
+
+ def = this->add("infill_lock_depth", coFloat);
+ def->label = L("Infill lock depth");
+ def->category = L("Strength");
+ def->tooltip = L("The parameter sets the overlapping depth between the interior and skin.");
+ def->sidetext = L("mm");
+ def->min = 0;
+ def->max = 100;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloat(1.0));
+
+ def = this->add("skin_infill_line_width", coFloatOrPercent);
+ def->label = L("Skin line width");
+ def->category = L("Strength");
+ def->tooltip = L("Adjust the line width of the selected skin paths.");
+ def->sidetext = L("mm");
+ def->ratio_over = "nozzle_diameter";
+ def->min = 0;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloatOrPercent(0.4, false));
+
+ def = this->add("skeleton_infill_line_width", coFloatOrPercent);
+ def->label = L("Skeleton line width");
+ def->category = L("Strength");
+ def->tooltip = L("Adjust the line width of the selected skeleton paths.");
+ def->sidetext = L("mm");
+ def->ratio_over = "nozzle_diameter";
+ def->min = 0;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloatOrPercent(0.4, false));
+
+ def = this->add("symmetric_infill_y_axis", coBool);
+ def->label = L("Symmetric infill y axis");
+ def->category = L("Strength");
+ def->tooltip = L("If the model has two parts that are symmetric about the y-axis,"
+ " and you want these parts to have symmetric textures, please click this option on one of the parts.");
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionBool(false));
+
// Orca: max layer height for combined infill
def = this->add("infill_combination_max_layer_height", coFloatOrPercent);
def->label = L("Infill combination - Max layer height");
@@ -6657,33 +6766,31 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
else if (value == "0"){
value = "ensure_moderate";
}
- }
- else if (opt_key == "sparse_infill_anchor") {
+ } else if (opt_key == "rotate_solid_infill_direction") {
+ opt_key = "solid_infill_rotate_template";
+ if (value == "1") {
+ value = "0,90";
+ } else if (value == "0") {
+ value = "0";
+ }
+ } else if (opt_key == "sparse_infill_anchor") {
opt_key = "infill_anchor";
- }
- else if (opt_key == "sparse_infill_anchor_max") {
+ } else if (opt_key == "sparse_infill_anchor_max") {
opt_key = "infill_anchor_max";
- }
- else if (opt_key == "chamber_temperatures") {
+ } else if (opt_key == "chamber_temperatures") {
opt_key = "chamber_temperature";
- }
- else if (opt_key == "thumbnail_size") {
+ } else if (opt_key == "thumbnail_size") {
opt_key = "thumbnails";
- }
- else if (opt_key == "top_one_wall_type" && value != "none") {
+ } else if (opt_key == "top_one_wall_type" && value != "none") {
opt_key = "only_one_wall_top";
value = "1";
- }
- else if (opt_key == "initial_layer_flow_ratio") {
+ } else if (opt_key == "initial_layer_flow_ratio") {
opt_key = "bottom_solid_infill_flow_ratio";
- }
- else if(opt_key == "ironing_direction") {
+ } else if (opt_key == "ironing_direction") {
opt_key = "ironing_angle";
- }
- else if(opt_key == "counterbole_hole_bridging") {
+ } else if (opt_key == "counterbole_hole_bridging") {
opt_key = "counterbore_hole_bridging";
- }
- else if (opt_key == "draft_shield" && value == "limited") {
+ } else if (opt_key == "draft_shield" && value == "limited") {
value = "disabled";
}
@@ -7304,7 +7411,9 @@ std::map validate(const FullPrintConfig &cfg, bool und
"internal_solid_infill_line_width",
"top_surface_line_width",
"support_line_width",
- "initial_layer_line_width" };
+ "initial_layer_line_width",
+ "skin_infill_line_width",
+ "skeleton_infill_line_width"};
for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) {
std::string key(widths[i]);
if (cfg.get_abs_value(key, max_nozzle_diameter) > 2.5 * max_nozzle_diameter) {
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index ffd97c0ac9..b4ce02309b 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -60,7 +60,7 @@ enum AuthorizationType {
enum InfillPattern : int {
ipConcentric, ipRectilinear, ipGrid, ip2DLattice, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipTpmsD, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip2DHoneycomb, ip3DHoneycomb,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
- ipLightning, ipCrossHatch, ipQuarterCubic,
+ ipLightning, ipCrossHatch, ipQuarterCubic, ipZigZag, ipCrossZag, ipLockedZag,
ipCount,
};
@@ -946,7 +946,10 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, outer_wall_speed))
((ConfigOptionFloat, infill_direction))
((ConfigOptionFloat, solid_infill_direction))
- ((ConfigOptionBool, rotate_solid_infill_direction))
+ ((ConfigOptionString, solid_infill_rotate_template))
+ ((ConfigOptionBool, symmetric_infill_y_axis))
+ ((ConfigOptionFloat, infill_shift_step))
+ ((ConfigOptionString, sparse_infill_rotate_template))
((ConfigOptionPercent, sparse_infill_density))
((ConfigOptionEnum, sparse_infill_pattern))
((ConfigOptionFloat, lattice_angle_1))
@@ -966,7 +969,12 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionPercent, infill_wall_overlap))
((ConfigOptionPercent, top_bottom_infill_wall_overlap))
((ConfigOptionFloat, sparse_infill_speed))
- //BBS
+ ((ConfigOptionPercent, skeleton_infill_density))
+ ((ConfigOptionPercent, skin_infill_density))
+ ((ConfigOptionFloat, infill_lock_depth))
+ ((ConfigOptionFloat, skin_infill_depth))
+ ((ConfigOptionFloatOrPercent, skin_infill_line_width))
+ ((ConfigOptionFloatOrPercent, skeleton_infill_line_width))
((ConfigOptionBool, infill_combination))
// Orca:
((ConfigOptionFloatOrPercent, infill_combination_max_layer_height))
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index 5db480347e..8b121f0bd9 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -1073,9 +1073,10 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "sparse_infill_filament"
|| opt_key == "solid_infill_filament"
|| opt_key == "sparse_infill_line_width"
+ || opt_key == "skin_infill_line_width"
+ || opt_key == "skeleton_infill_line_width"
|| opt_key == "infill_direction"
|| opt_key == "solid_infill_direction"
- || opt_key == "rotate_solid_infill_direction"
|| opt_key == "ensure_vertical_shell_thickness"
|| opt_key == "bridge_angle"
|| opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override
@@ -1097,7 +1098,15 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "lattice_angle_2"
|| opt_key == "infill_overhang_angle") {
steps.emplace_back(posInfill);
- } else if (opt_key == "sparse_infill_pattern") {
+ } else if (opt_key == "sparse_infill_pattern"
+ || opt_key == "symmetric_infill_y_axis"
+ || opt_key == "infill_shift_step"
+ || opt_key == "sparse_infill_rotate_template"
+ || opt_key == "solid_infill_rotate_template"
+ || opt_key == "skeleton_infill_density"
+ || opt_key == "skin_infill_density"
+ || opt_key == "infill_lock_depth"
+ || opt_key == "skin_infill_depth") {
steps.emplace_back(posPrepareInfill);
} else if (opt_key == "sparse_infill_density") {
// One likely wants to reslice only when switching between zero infill to simulate boolean difference (subtracting volumes),
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index dc73eb9454..134538137d 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -472,6 +472,20 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
apply(config, &new_conf);
is_msg_dlg_already_exist = false;
}
+
+ // layer_height shouldn't be equal to zero
+ float skin_depth = config->opt_float("skin_infill_depth");
+ if (config->opt_float("infill_lock_depth") > skin_depth) {
+ const wxString msg_text = _(L("lock depth should smaller than skin depth.\nReset to 50% of skin depth"));
+ MessageDialog dialog(m_msg_dlg_parent, msg_text, "", wxICON_WARNING | wxOK);
+ DynamicPrintConfig new_conf = *config;
+ is_msg_dlg_already_exist = true;
+ dialog.ShowModal();
+ new_conf.set_key_value("infill_lock_depth", new ConfigOptionFloat(skin_depth / 2));
+ apply(config, &new_conf);
+ is_msg_dlg_already_exist = false;
+ }
+
}
void ConfigManipulation::apply_null_fff_config(DynamicPrintConfig *config, std::vector const &keys, std::map const &configs)
@@ -526,7 +540,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
bool have_infill = config->option("sparse_infill_density")->value > 0;
// sparse_infill_filament uses the same logic as in Print::extruders()
for (auto el : { "sparse_infill_pattern", "infill_combination",
- "minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max"})
+ "minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max","infill_shift_step","sparse_infill_rotate_template","symmetric_infill_y_axis"})
toggle_line(el, have_infill);
bool have_combined_infill = config->opt_bool("infill_combination") && have_infill;
@@ -539,6 +553,19 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
bool has_infill_anchors = have_infill && config->option("infill_anchor_max")->value > 0 && infill_anchor;
toggle_field("infill_anchor", has_infill_anchors);
+ //cross zag
+ bool is_cross_zag = config->option>("sparse_infill_pattern")->value == InfillPattern::ipCrossZag;
+ bool is_locked_zig = config->option>("sparse_infill_pattern")->value == InfillPattern::ipLockedZag;
+
+ toggle_line("infill_shift_step", is_cross_zag || is_locked_zig);
+
+ for (auto el : { "skeleton_infill_density", "skin_infill_density", "infill_lock_depth", "skin_infill_depth","skin_infill_line_width", "skeleton_infill_line_width" })
+ toggle_line(el, is_locked_zig);
+
+ bool is_zig_zag = config->option>("sparse_infill_pattern")->value == InfillPattern::ipZigZag;
+
+ toggle_line("symmetric_infill_y_axis", is_zig_zag || is_cross_zag || is_locked_zig);
+
bool has_spiral_vase = config->opt_bool("spiral_mode");
toggle_line("spiral_mode_smooth", has_spiral_vase);
toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && config->opt_bool("spiral_mode_smooth"));
@@ -552,7 +579,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
for (auto el : { "infill_direction", "sparse_infill_line_width",
"sparse_infill_speed", "bridge_speed", "internal_bridge_speed", "bridge_angle", "internal_bridge_angle",
- "solid_infill_direction", "rotate_solid_infill_direction", "internal_solid_infill_pattern", "solid_infill_filament",
+ "solid_infill_direction", "solid_infill_rotate_template", "internal_solid_infill_pattern", "solid_infill_filament",
})
toggle_field(el, have_infill || has_solid_infill);
@@ -823,6 +850,22 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
for (auto el : { "lattice_angle_1", "lattice_angle_2"})
toggle_line(el, lattice_options);
+ //Orca: hide rotate template for solid infill if not support
+ const auto _sparse_infill_pattern = config->option>("sparse_infill_pattern")->value;
+ bool show_sparse_infill_rotate_template = _sparse_infill_pattern == ipRectilinear || _sparse_infill_pattern == ipLine ||
+ _sparse_infill_pattern == ipZigZag || _sparse_infill_pattern == ipCrossZag ||
+ _sparse_infill_pattern == ipLockedZag;
+
+ toggle_line("sparse_infill_rotate_template", show_sparse_infill_rotate_template);
+
+ //Orca: hide rotate template for solid infill if not support
+ const auto _solid_infill_pattern = config->option>("internal_solid_infill_pattern")->value;
+ bool show_solid_infill_rotate_template = _solid_infill_pattern == ipRectilinear || _solid_infill_pattern == ipMonotonic ||
+ _solid_infill_pattern == ipMonotonicLine || _solid_infill_pattern == ipAlignedRectilinear;
+
+ toggle_line("solid_infill_rotate_template", show_solid_infill_rotate_template);
+
+
toggle_line("infill_overhang_angle", config->opt_enum("sparse_infill_pattern") == InfillPattern::ip2DHoneycomb);
}
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index 7e5f229f1a..d7c9408103 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -430,6 +430,16 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
str = str_out;
set_value(str, true);
}
+ } else if (m_opt.opt_key == "sparse_infill_rotate_template" || m_opt.opt_key == "solid_infill_rotate_template") {
+ if (!ConfigOptionFloats::validate_string(str.utf8_string())) {
+ show_error(m_parent, format_wxstr(_L("This parameter expects a comma-delimited list of numbers. E.g, \"0,90\".")));
+ wxString old_value(boost::any_cast(m_value));
+ this->set_value(old_value, true); // Revert to previous value
+ } else {
+ // Valid string, so update m_value with the new string from the control.
+ m_value = into_u8(str);
+ }
+ break;
}
m_value = into_u8(str);
diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp
index 237c8c9604..d5d5de76a9 100644
--- a/src/slic3r/GUI/GUI_Factories.cpp
+++ b/src/slic3r/GUI/GUI_Factories.cpp
@@ -107,7 +107,7 @@ std::map> SettingsFactory::PART_CAT
{ L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},
{"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},
{"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"lattice_angle_1", "",1},{"lattice_angle_2", "",1},{"infill_overhang_angle", "",1},{"infill_anchor", "",1},{"infill_anchor_max", "",1},{"top_surface_pattern", "",1},{"bottom_surface_pattern", "",1}, {"internal_solid_infill_pattern", "",1},
- {"infill_combination", "",1}, {"infill_combination_max_layer_height", "",1}, {"infill_wall_overlap", "",1},{"top_bottom_infill_wall_overlap", "",1}, {"solid_infill_direction", "",1}, {"rotate_solid_infill_direction", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"internal_bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1}
+ {"infill_combination", "",1}, {"infill_combination_max_layer_height", "",1}, {"infill_wall_overlap", "",1},{"top_bottom_infill_wall_overlap", "",1}, {"solid_infill_direction", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"internal_bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1}
}},
{ L("Speed"), {{"outer_wall_speed", "",1},{"inner_wall_speed", "",2},{"sparse_infill_speed", "",3},{"top_surface_speed", "",4}, {"internal_solid_infill_speed", "",5},
{"enable_overhang_speed", "",6}, {"overhang_1_4_speed", "",7}, {"overhang_2_4_speed", "",8}, {"overhang_3_4_speed", "",9}, {"overhang_4_4_speed", "",10},
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index fc7f6c7305..30e1f48d3c 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -9920,7 +9920,6 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i
_obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f));
_obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45));
_obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135));
- _obj->config.set_key_value("rotate_solid_infill_direction", new ConfigOptionBool(true));
_obj->config.set_key_value("ironing_type", new ConfigOptionEnum(IroningType::NoIroning));
_obj->config.set_key_value("internal_solid_infill_speed", new ConfigOptionFloat(internal_solid_speed));
_obj->config.set_key_value("top_surface_speed", new ConfigOptionFloat(top_surface_speed));
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 7b645a891f..730a3fda96 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -1470,6 +1470,9 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
}
}
+ if (opt_key == "single_extruder_multi_material" || opt_key == "extruders_count" )
+ update_wiping_button_visibility();
+
if (opt_key == "pellet_flow_coefficient")
{
@@ -1483,6 +1486,44 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
}
+ if (opt_key == "single_extruder_multi_material" ){
+ const auto bSEMM = m_config->opt_bool("single_extruder_multi_material");
+ wxGetApp().sidebar().show_SEMM_buttons(bSEMM);
+ wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
+ }
+
+ if(opt_key == "purge_in_prime_tower")
+ wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
+
+
+ if (opt_key == "enable_prime_tower") {
+ auto timelapse_type = m_config->option>("timelapse_type");
+ bool timelapse_enabled = timelapse_type->value == TimelapseType::tlSmooth;
+ if (!boost::any_cast(value) && timelapse_enabled) {
+ MessageDialog dlg(wxGetApp().plater(), _L("A prime tower is required for smooth timelapse. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"),
+ _L("Warning"), wxICON_WARNING | wxYES | wxNO);
+ if (dlg.ShowModal() == wxID_NO) {
+ DynamicPrintConfig new_conf = *m_config;
+ new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true));
+ m_config_manipulation.apply(m_config, &new_conf);
+ }
+ wxGetApp().plater()->update();
+ }
+ bool is_precise_z_height = m_config->option("precise_z_height")->value;
+ if (boost::any_cast(value) && is_precise_z_height) {
+ MessageDialog dlg(wxGetApp().plater(), _L("Enabling both precise Z height and the prime tower may cause the size of prime tower to increase. Do you still want to enable?"),
+ _L("Warning"), wxICON_WARNING | wxYES | wxNO);
+ if (dlg.ShowModal() == wxID_NO) {
+ DynamicPrintConfig new_conf = *m_config;
+ new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(false));
+ m_config_manipulation.apply(m_config, &new_conf);
+ }
+ wxGetApp().plater()->update();
+ }
+ update_wiping_button_visibility();
+ }
+
+
if (opt_key == "single_extruder_multi_material" ){
const auto bSEMM = m_config->opt_bool("single_extruder_multi_material");
wxGetApp().sidebar().show_SEMM_buttons(bSEMM);
@@ -2165,20 +2206,30 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Infill"), L"param_infill");
optgroup->append_single_option_line("sparse_infill_density", "strength_settings_infill#sparse-infill-density");
optgroup->append_single_option_line("sparse_infill_pattern", "strength_settings_infill#sparse-infill-pattern");
+ optgroup->append_single_option_line("infill_direction");
+ optgroup->append_single_option_line("sparse_infill_rotate_template");
+ optgroup->append_single_option_line("skin_infill_density");
+ optgroup->append_single_option_line("skeleton_infill_density");
+ optgroup->append_single_option_line("infill_lock_depth");
+ optgroup->append_single_option_line("skin_infill_depth");
+ optgroup->append_single_option_line("skin_infill_line_width", "parameter/line-width");
+ optgroup->append_single_option_line("skeleton_infill_line_width", "parameter/line-width");
+ optgroup->append_single_option_line("symmetric_infill_y_axis");
+ optgroup->append_single_option_line("infill_shift_step");
+
optgroup->append_single_option_line("lattice_angle_1");
optgroup->append_single_option_line("lattice_angle_2");
optgroup->append_single_option_line("infill_overhang_angle");
optgroup->append_single_option_line("infill_anchor_max");
optgroup->append_single_option_line("infill_anchor");
optgroup->append_single_option_line("internal_solid_infill_pattern");
+ optgroup->append_single_option_line("solid_infill_direction");
+ optgroup->append_single_option_line("solid_infill_rotate_template");
optgroup->append_single_option_line("gap_fill_target");
optgroup->append_single_option_line("filter_out_gap_fill");
optgroup->append_single_option_line("infill_wall_overlap");
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
- optgroup->append_single_option_line("infill_direction");
- optgroup->append_single_option_line("solid_infill_direction");
- optgroup->append_single_option_line("rotate_solid_infill_direction");
optgroup->append_single_option_line("bridge_angle");
optgroup->append_single_option_line("internal_bridge_angle"); // ORCA: Internal bridge angle override
optgroup->append_single_option_line("minimum_sparse_infill_area");