From 963f8d86b73c4ac4a96f445af4a8ce94d4182f6c Mon Sep 17 00:00:00 2001 From: Matthias Nott Date: Mon, 9 Feb 2026 20:38:46 +0100 Subject: [PATCH 01/28] feat: Add Z Anti-Aliasing (ZAA) contouring support Port Z Anti-Aliasing from BambuStudio-ZAA (https://github.com/adob/BambuStudio-ZAA) to OrcaSlicer. ZAA eliminates stair-stepping on curved and sloped top surfaces by raycasting each extrusion point against the original 3D mesh and micro-adjusting Z height to follow the actual surface geometry. Key changes: - Add ContourZ.cpp raycasting algorithm (~330 lines) - Extend geometry with 3D support (Point3, Line3, Polyline3, MultiPoint3) - Template arc fitting for 2D/3D compatibility - Change ExtrusionPath::polyline from Polyline to Polyline3 - Add 5 ZAA config options (zaa_enabled, zaa_min_z, etc.) - Add posContouring pipeline step in PrintObject - Update GCode writer for 3D coordinate output - Add ZAA settings UI in Print Settings > Quality - Add docs/ZAA.md with usage and implementation details ZAA is opt-in and disabled by default. When disabled, the slicing pipeline is unchanged. --- .gitignore | 1 + docs/ZAA.md | 41 +++ src/libslic3r/ArcFitter.cpp | 56 ++- src/libslic3r/ArcFitter.hpp | 2 + src/libslic3r/BoundingBox.hpp | 13 +- src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Circle.cpp | 113 ++++++ src/libslic3r/Circle.hpp | 9 + src/libslic3r/ClipperUtils.cpp | 13 + src/libslic3r/ClipperUtils.hpp | 3 + src/libslic3r/ContourZ.cpp | 333 ++++++++++++++++++ src/libslic3r/ExtrusionEntity.cpp | 98 ++++-- src/libslic3r/ExtrusionEntity.hpp | 110 ++++-- src/libslic3r/ExtrusionEntityCollection.hpp | 4 +- src/libslic3r/ExtrusionSimulator.cpp | 2 +- src/libslic3r/Fill/Fill.cpp | 4 + src/libslic3r/Fill/FillBase.cpp | 4 +- src/libslic3r/Fill/FillBase.hpp | 1 + src/libslic3r/GCode.cpp | 127 ++++--- src/libslic3r/GCode.hpp | 13 +- src/libslic3r/GCode/ConflictChecker.hpp | 2 +- src/libslic3r/GCode/ExtrusionProcessor.hpp | 14 +- src/libslic3r/GCode/PrintExtents.cpp | 6 +- src/libslic3r/GCode/SeamPlacer.cpp | 6 +- src/libslic3r/Layer.hpp | 5 + src/libslic3r/Line.hpp | 48 ++- src/libslic3r/MultiPoint.cpp | 119 +++++++ src/libslic3r/MultiPoint.hpp | 30 +- src/libslic3r/PerimeterGenerator.cpp | 30 +- src/libslic3r/Point.cpp | 77 ++++ src/libslic3r/Point.hpp | 121 ++++++- src/libslic3r/Polyline.cpp | 235 ++++++++++++ src/libslic3r/Polyline.hpp | 62 ++++ src/libslic3r/Preset.cpp | 2 + src/libslic3r/Print.cpp | 25 +- src/libslic3r/Print.hpp | 11 +- src/libslic3r/PrintConfig.cpp | 51 +++ src/libslic3r/PrintConfig.hpp | 10 + src/libslic3r/PrintObject.cpp | 53 ++- src/libslic3r/PrintObjectSlice.cpp | 8 + src/libslic3r/ShortestPath.cpp | 9 +- src/libslic3r/ShortestPath.hpp | 1 + src/libslic3r/Support/SupportCommon.cpp | 7 +- src/libslic3r/Support/SupportMaterial.cpp | 4 +- src/libslic3r/VariableWidth.cpp | 22 +- src/libslic3r/libslic3r_version.h.in | 1 + src/slic3r/GUI/3DScene.cpp | 6 +- src/slic3r/GUI/GUI_App.cpp | 39 +- src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/GUI_Factories.cpp | 4 +- src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp | 6 +- src/slic3r/GUI/MainFrame.cpp | 14 + src/slic3r/GUI/OptionsGroup.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 13 +- src/slic3r/GUI/Search.cpp | 20 +- src/slic3r/GUI/Tab.cpp | 8 + version.inc | 1 + 57 files changed, 1817 insertions(+), 204 deletions(-) create mode 100644 docs/ZAA.md create mode 100644 src/libslic3r/ContourZ.cpp diff --git a/.gitignore b/.gitignore index 5b369a47fe..7268d4a273 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ test.js /.cache/ .clangd internal_docs/ +resources/nonplanar/ diff --git a/docs/ZAA.md b/docs/ZAA.md new file mode 100644 index 0000000000..dbeecc6548 --- /dev/null +++ b/docs/ZAA.md @@ -0,0 +1,41 @@ +# Z Anti-Aliasing (ZAA) — Z Contouring + +ZAA eliminates stair-stepping on curved and sloped top surfaces by adjusting the Z height of each extrusion point to follow the actual 3D model surface. + +Instead of printing flat horizontal layers, ZAA raycasts each point of the toolpath against the original mesh and micro-adjusts its Z coordinate to match the true surface geometry. The result is visibly smoother surfaces on domes, chamfers, and shallow slopes — without post-processing. + +This is a port of the ZAA implementation from [BambuStudio-ZAA](https://github.com/adob/BambuStudio-ZAA) by adob. + +## Configuration + +ZAA adds five settings under **Print Settings > Quality**: + +| Setting | Type | Default | Description | +|---------|------|---------|-------------| +| `zaa_enabled` | bool | off | Master enable/disable switch | +| `zaa_min_z` | float | 0.06 mm | Minimum Z layer height; also controls the slicing plane offset | +| `zaa_minimize_perimeter_height` | float | 35° | Reduce perimeter heights on slopes below this angle (0 = disabled) | +| `zaa_dont_alternate_fill_direction` | bool | off | Keep fill direction consistent instead of alternating per layer | +| `zaa_region_disable` | bool | off | Disable ZAA for a specific print region/material | + +## How It Works + +1. The slicer slices normally, then runs a **posContouring** step on each layer. +2. `ContourZ.cpp` raycasts every extrusion point vertically against the source mesh. +3. Each point's Z is adjusted to the mesh intersection, converting flat `Polyline` paths into `Polyline3` paths that carry per-point Z coordinates. +4. The G-code writer emits the adjusted Z values, so the printer follows the true surface. + +## Key Implementation Details + +- **Core algorithm**: `src/libslic3r/ContourZ.cpp` (~330 lines) +- **3D geometry**: `Point3`, `Line3`, `Polyline3`, `MultiPoint3` extend the existing 2D types +- **Pipeline step**: `posContouring` in `PrintObject.cpp`, runs after perimeter/infill generation +- **G-code output**: `GCode.cpp` writes per-point Z when `path.z_contoured` is set +- **Arc fitting**: Templated to work with both 2D and 3D geometry +- **ExtrusionPath change**: `polyline` field changed from `Polyline` to `Polyline3` + +## Testing + +1. Load a model with curved top surfaces (spheres, domes, chamfered edges) +2. Enable **Z contouring** in Print Settings > Quality +3. Slice and inspect the G-code — Z values should vary within each layer on contoured surfaces diff --git a/src/libslic3r/ArcFitter.cpp b/src/libslic3r/ArcFitter.cpp index cdfd708b10..b15548ee84 100644 --- a/src/libslic3r/ArcFitter.cpp +++ b/src/libslic3r/ArcFitter.cpp @@ -1,4 +1,5 @@ #include "ArcFitter.hpp" +#include "Point.hpp" #include "Polyline.hpp" #include @@ -6,7 +7,17 @@ namespace Slic3r { -void ArcFitter::do_arc_fitting(const Points& points, std::vector& result, double tolerance) +// Helper functions to dispatch to the correct douglas_peucker implementation +static inline Points douglas_peucker_helper(const Points &points, double tolerance) { + return MultiPoint::_douglas_peucker(points, tolerance); +} + +static inline Points3 douglas_peucker_helper(const Points3 &points, double tolerance) { + return MultiPoint3::_douglas_peucker(points, tolerance); +} + +template +static void do_arc_fitting_tmpl(const POINTS& points, std::vector& result, double tolerance) { #ifdef DEBUG_ARC_FITTING static int irun = 0; @@ -39,7 +50,7 @@ void ArcFitter::do_arc_fitting(const Points& points, std::vector 2) { //BBS: althought current point_stack can't be fit as arc, //but previous must can be fit if removing the top in stack, so save last arc - result.emplace_back(std::move(PathFittingData{ front_index, + result.emplace_back(PathFittingData{ front_index, back_index - 1, last_arc.direction == ArcDirection::Arc_Dir_CCW ? EMovePathType::Arc_move_ccw : EMovePathType::Arc_move_cw, - last_arc })); + last_arc }); } else { //BBS: save the first segment as line move when 3 point-line can't be fit as arc move if (result.empty() || result.back().path_type != EMovePathType::Linear_move) @@ -94,7 +105,18 @@ void ArcFitter::do_arc_fitting(const Points& points, std::vector& result, double tolerance) +void ArcFitter::do_arc_fitting(const Points &points, std::vector& result, double tolerance) +{ + do_arc_fitting_tmpl(points, result, tolerance); +} + +void ArcFitter::do_arc_fitting(const Points3 &points, std::vector& result, double tolerance) +{ + do_arc_fitting_tmpl(points, result, tolerance); +} + +template +static void do_arc_fitting_and_simplify_tmpl(POINTS &points, std::vector& result, double tolerance) { //BBS: 1 do arc fit first if (abs(tolerance) > SCALED_EPSILON) @@ -106,12 +128,12 @@ void ArcFitter::do_arc_fitting_and_simplify(Points& points, std::vector reduce_count(result.size(), 0); @@ -124,11 +146,11 @@ void ArcFitter::do_arc_fitting_and_simplify(Points& points, std::vector& result, double tolerance) +{ + do_arc_fitting_and_simplify_tmpl(points, result, tolerance); +} + +void ArcFitter::do_arc_fitting_and_simplify(Points3& points, std::vector& result, double tolerance) +{ + do_arc_fitting_and_simplify_tmpl(points, result, tolerance); +} + } \ No newline at end of file diff --git a/src/libslic3r/ArcFitter.hpp b/src/libslic3r/ArcFitter.hpp index f2b2ee49d8..6672b0b771 100644 --- a/src/libslic3r/ArcFitter.hpp +++ b/src/libslic3r/ArcFitter.hpp @@ -42,9 +42,11 @@ class ArcFitter { public: //BBS: this function is used to check the point list and return which part can fit as arc, which part should be line static void do_arc_fitting(const Points& points, std::vector &result, double tolerance); + static void do_arc_fitting(const Points3& points, std::vector &result, double tolerance); //BBS: this function is used to check the point list and return which part can fit as arc, which part should be line. //By the way, it also use DP simplify to reduce point of straight part and only keep the start and end point of arc. static void do_arc_fitting_and_simplify(Points& points, std::vector& result, double tolerance); + static void do_arc_fitting_and_simplify(Points3& points, std::vector& result, double tolerance); }; } diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index d6baa27012..26b840ade6 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -236,7 +236,18 @@ class BoundingBox3 : public BoundingBox3Base public: BoundingBox3() : BoundingBox3Base() {} BoundingBox3(const Vec3crd &pmin, const Vec3crd &pmax) : BoundingBox3Base(pmin, pmax) {} - BoundingBox3(const Points3& points) : BoundingBox3Base(points) {} + BoundingBox3(const Points3& points) : BoundingBox3Base() { + if (!points.empty()) { + this->min = points.front(); + this->max = points.front(); + for (const auto &p : points) { + this->min = this->min.cwiseMin(static_cast(p)); + this->max = this->max.cwiseMax(static_cast(p)); + } + this->defined = true; + } + } + BoundingBox3(const std::vector& points) : BoundingBox3Base(points) {} }; class BoundingBoxf : public BoundingBoxBase diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 4ab335879d..d5d5c66c76 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -100,6 +100,7 @@ set(lisbslic3r_sources CommonDefs.hpp Config.cpp Config.hpp + ContourZ.cpp CustomGCode.cpp CustomGCode.hpp CutUtils.cpp diff --git a/src/libslic3r/Circle.cpp b/src/libslic3r/Circle.cpp index bcd7fc47c1..c277a67795 100644 --- a/src/libslic3r/Circle.cpp +++ b/src/libslic3r/Circle.cpp @@ -3,6 +3,7 @@ #include #include #include "Geometry.hpp" +#include "Polygon.hpp" //BBS: Refer to ArcWelderLib for the arc fitting functions @@ -94,6 +95,12 @@ bool Circle::try_create_circle(const Points& points, const double max_radius, co return found_circle; } +bool Circle::try_create_circle(const Points3& points, const double max_radius, const double tolerance, Circle& new_circle) +{ + return Circle::try_create_circle(to_points(points), max_radius, tolerance, new_circle); +} + + double Circle::get_polar_radians(const Point& p1) const { double polar_radians = atan2(p1.y() - center.y(), p1.x() - center.x()); @@ -291,6 +298,31 @@ bool ArcSegment::try_create_arc( return false; } +bool ArcSegment::try_create_arc( + const Points3& points, + ArcSegment& target_arc, + double approximate_length, + double max_radius, + double tolerance, + double path_tolerance_percent) +{ + Circle test_circle = (Circle)target_arc; + if (!Circle::try_create_circle(points, max_radius, tolerance, test_circle)) + return false; + + int mid_point_index = ((points.size() - 2) / 2) + 1; + ArcSegment test_arc; + if (!ArcSegment::try_create_arc(test_circle, points[0].to_point(), points[mid_point_index].to_point(), points[points.size() - 1].to_point(), test_arc, approximate_length, path_tolerance_percent)) + return false; + + if (ArcSegment::are_points_within_slice(test_arc, points)) + { + target_arc = test_arc; + return true; + } + return false; +} + bool ArcSegment::try_create_arc( const Circle& c, const Point& start_point, @@ -455,6 +487,87 @@ bool ArcSegment::are_points_within_slice(const ArcSegment& test_arc, const Point return true; } +bool ArcSegment::are_points_within_slice(const ArcSegment& test_arc, const Points3& points) +{ + //BBS: Check all the points and see if they fit inside of the angles + double previous_polar = test_arc.polar_start_theta; + bool will_cross_zero = false; + bool crossed_zero = false; + const int point_count = points.size(); + + Vec2d start_norm(((double)test_arc.start_point.x() - (double)test_arc.center.x()) / test_arc.radius, + ((double)test_arc.start_point.y() - (double)test_arc.center.y()) / test_arc.radius); + Vec2d end_norm(((double)test_arc.end_point.x() - (double)test_arc.center.x()) / test_arc.radius, + ((double)test_arc.end_point.y() - (double)test_arc.center.y()) / test_arc.radius); + + if (test_arc.direction == ArcDirection::Arc_Dir_CCW) + will_cross_zero = test_arc.polar_start_theta > test_arc.polar_end_theta; + else + will_cross_zero = test_arc.polar_start_theta < test_arc.polar_end_theta; + + //BBS: check if point 1 to point 2 cross zero + double polar_test; + for (int index = point_count - 2; index < point_count; index++) + { + if (index < point_count - 1) + polar_test = test_arc.get_polar_radians(points[index].to_point()); + else + polar_test = test_arc.polar_end_theta; + + //BBS: First ensure the test point is within the arc + if (test_arc.direction == ArcDirection::Arc_Dir_CCW) + { + //BBS: Only check to see if we are within the arc if this isn't the endpoint + if (index < point_count - 1) { + if (will_cross_zero) { + if (!(polar_test > test_arc.polar_start_theta || polar_test < test_arc.polar_end_theta)) + return false; + } else if (!(test_arc.polar_start_theta < polar_test && polar_test < test_arc.polar_end_theta)) + return false; + } + //BBS: check the angles are increasing + if (previous_polar > polar_test) { + if (!will_cross_zero) + return false; + + //BBS: Allow the angle to cross zero once + if (crossed_zero) + return false; + crossed_zero = true; + } + } else { + if (index < point_count - 1) { + if (will_cross_zero) { + if (!(polar_test < test_arc.polar_start_theta || polar_test > test_arc.polar_end_theta)) + return false; + } else if (!(test_arc.polar_start_theta > polar_test && polar_test > test_arc.polar_end_theta)) + return false; + } + //BBS: Now make sure the angles are decreasing + if (previous_polar < polar_test) + { + if (!will_cross_zero) + return false; + //BBS: Allow the angle to cross zero once + if (crossed_zero) + return false; + crossed_zero = true; + } + } + + // BBS: check if the segment intersects either of the vector from the center of the circle to the endpoints of the arc + Line segmemt(points[index - 1].to_point(), points[index].to_point()); + if ((index != 1 && ray_intersects_segment(test_arc.center, start_norm, segmemt)) || + (index != point_count - 1 && ray_intersects_segment(test_arc.center, end_norm, segmemt))) + return false; + previous_polar = polar_test; + } + //BBS: Ensure that all arcs that cross zero + if (will_cross_zero != crossed_zero) + return false; + return true; +} + // BBS: this function is used to detect whether a ray cross the segment bool ArcSegment::ray_intersects_segment(const Point &rayOrigin, const Vec2d &rayDirection, const Line& segment) { diff --git a/src/libslic3r/Circle.hpp b/src/libslic3r/Circle.hpp index 8c649181dd..c06b197e19 100644 --- a/src/libslic3r/Circle.hpp +++ b/src/libslic3r/Circle.hpp @@ -28,6 +28,7 @@ public: static bool try_create_circle(const Point &p1, const Point &p2, const Point &p3, const double max_radius, Circle& new_circle); static bool try_create_circle(const Points& points, const double max_radius, const double tolerance, Circle& new_circle); + static bool try_create_circle(const Points3& points, const double max_radius, const double tolerance, Circle& new_circle); double get_polar_radians(const Point& p1) const; bool is_over_deviation(const Points& points, const double tolerance); bool get_deviation_sum_squared(const Points& points, const double tolerance, double& sum_deviation); @@ -111,8 +112,16 @@ public: double max_radius = DEFAULT_SCALED_MAX_RADIUS, double tolerance = DEFAULT_SCALED_RESOLUTION, double path_tolerance_percent = DEFAULT_ARC_LENGTH_PERCENT_TOLERANCE); + static bool try_create_arc( + const Points3 &points, + ArcSegment& target_arc, + double approximate_length, + double max_radius = DEFAULT_SCALED_MAX_RADIUS, + double tolerance = DEFAULT_SCALED_RESOLUTION, + double path_tolerance_percent = DEFAULT_ARC_LENGTH_PERCENT_TOLERANCE); static bool are_points_within_slice(const ArcSegment& test_arc, const Points &points); + static bool are_points_within_slice(const ArcSegment& test_arc, const Points3 &points); // BBS: this function is used to detect whether a ray cross the segment static bool ray_intersects_segment(const Point& rayOrigin, const Vec2d& rayDirection, const Line& segment); // BBS: these three functions are used to calculate related arguments of arc in unscale_field. diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 2440983f17..2f97e08f53 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -411,6 +411,19 @@ Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type) { assert(delta > 0); return to_polygons(clipper_union(raw_offset_polyline(ClipperUtils::SinglePathProvider(polyline.points), delta, joinType, miterLimit, end_type))); } + +Slic3r::Polygons offset(const Slic3r::Polyline3 &polyline, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type) +{ + assert(delta > 0); + return to_polygons( + clipper_union( + raw_offset_polyline( + ClipperUtils::SinglePathProvider(polyline.to_polyline().points), + delta, + joinType, + miterLimit, + end_type))); +} Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type) { assert(delta > 0); return to_polygons(clipper_union(raw_offset_polyline(ClipperUtils::PolylinesProvider(polylines), delta, joinType, miterLimit, end_type))); } diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index cfe1f40935..9c2fa23926 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -334,6 +334,7 @@ Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, Clipp // Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better. // Input polygons for negative offset shall be "normalized": There must be no overlap / intersections between the input polygons. Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType); +Slic3r::Polygons offset(const Slic3r::Polyline3 &polyline, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType); Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType); Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit); Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit); @@ -524,6 +525,8 @@ Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip); Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip); +Slic3r::Polylines3 intersection_pl(const Slic3r::Polylines3 &subject, const Slic3r::Polygon &clip); +Slic3r::Polylines3 intersection_pl(const Slic3r::Polylines3 &subject, const Slic3r::ExPolygon &clip); inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip) { diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp new file mode 100644 index 0000000000..13a266573f --- /dev/null +++ b/src/libslic3r/ContourZ.cpp @@ -0,0 +1,333 @@ +#include "Exception.hpp" +#include "ExtrusionEntity.hpp" +#include "ExtrusionEntityCollection.hpp" +#include "Layer.hpp" +#include "Point.hpp" +#include "Print.hpp" +#include "SLA/IndexedMesh.hpp" +#include "libslic3r.h" +#include +#include +#include +#include + +namespace Slic3r { + +static void contour_extrusion_entity(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntity *extr); + +// static double lowest_z_within_distance(const Vec3d &normal, double dist) { +// const Vec3d p(0.0, 0.0, 0.0); +// Eigen::Vector3d n_unit = normal.normalized(); +// Eigen::Vector3d z_hat(0.0, 0.0, 1.0); + +// // Project the negative z-direction into the tangent plane +// Eigen::Vector3d v_dir = -z_hat + (z_hat.dot(n_unit)) * n_unit; + +// double norm_v = v_dir.norm(); +// if (norm_v == 0.0) { +// // Surface is horizontal, cannot go lower in z within tangent plane +// return p.z(); +// } + +// Eigen::Vector3d v = dist * v_dir / norm_v; +// Eigen::Vector3d q = p + v; +// return q.z(); +// } + +static double follow_slope_down(double angle_rad, double dist) { + return -dist * std::sin(angle_rad); +} + +static double slope_from_normal(const Eigen::Vector3d& normal) { + // Ensure the normal is normalized + Eigen::Vector3d n = normal.normalized(); + + // Compute angle between normal and z-axis + double angle_rad = std::acos(std::abs(n.z())); // angle between normal and vertical + return angle_rad; + + // calculate fall over dist + // double dist = 0.2; + // double z_dist = lowest_z(angle_rad, dist); + // printf("fall %f vs %f\n", z_dist, lowest_z_within_distance(normal, dist)); + + // double angle_deg = angle_rad * 180.0 / M_PI; + // return angle_deg; +} + +// const int LINE = 180; + +static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionPath &path) { + if (region->region().config().zaa_region_disable) { + return false; + } + + if (path.role() != erTopSolidInfill && path.role() != erIroning && path.role() != erExternalPerimeter && path.role() != erPerimeter) { + return false; + } + + Layer *layer = region->layer(); + coordf_t mesh_z = layer->print_z + mesh.ground_level(); + coordf_t min_z = layer->object()->config().zaa_min_z; + + const Points3 &points = path.polyline.points; + double resolution_mm = 0.1; + + coordf_t height = layer->height; + // std::cout << "LAYER " << (layer->id()+1) << std::endl; + // std::cout << "PRINT Z " << layer->print_z << std::endl; + // std::cout << "LAYER HEIGHT " << height << std::endl; + // std::cout << "EXTRUSION HEIGHT " << path.height << std::endl; + // std::cout << "EXTRUSION WIDTH " << path.width << std::endl; + // std::cout << "EXTRUSION ROLE: " << ExtrusionEntity::role_to_string(path.role()) << std::endl; + // std::cout << "FIRST POINT: " << path.polyline.first_point() << std::endl; + + double minimize_perimeter_height_angle = region->region().config().zaa_minimize_perimeter_height; + + Pointf3s contoured_points; + bool was_contoured = false; + // bool is_perimeter = path.role() == erExternalPerimeter || path.role() == erPerimeter || path.role() == erOverhangPerimeter; + + for (Points3::const_iterator it = points.begin(); it != points.end()-1; ++it) { + Vec2d p1d(unscale_(it->x()), unscale_(it->y())); + Vec2d p2d(unscale_((it+1)->x()), unscale_((it+1)->y())); + Linef line(p1d, p2d); + + double length_mm = line.length(); + int num_segments = int(std::ceil(length_mm / resolution_mm)); + Vec2d delta = line.vector(); + + for (int i = 0; i < num_segments+1; i++) { + Vec2d p = p1d + delta*i/num_segments; + + coordf_t x = p.x(); + coordf_t y = p.y(); + + sla::IndexedMesh::hit_result hit_up = mesh.query_ray_hit({x, y, mesh_z}, {0.0, 0.0, 1.0}); + sla::IndexedMesh::hit_result hit_down = mesh.query_ray_hit({x, y, mesh_z}, {0.0, 0.0, -1.0}); + + double up = hit_up.distance(); + double down = hit_down.distance(); + double d = up < down ? up : -down; + const Vec3d &normal = (up < down ? hit_up : hit_down).normal(); + + double max_up = min_z; + double min_down = -(height - min_z); + double half_width = path.width / 2.0; + if (path.role() == erIroning) { + max_up = height; + min_down = -(height + 0.1); + } + + double slope_rad = slope_from_normal(normal); + double slope_degrees = slope_rad * 180.0 / M_PI; + + if (d > min_down && minimize_perimeter_height_angle > 0 && minimize_perimeter_height_angle < slope_degrees && path.role() == erExternalPerimeter) { + double adjustment = follow_slope_down(slope_rad, half_width); + if (adjustment > 0) { + throw RuntimeError("ContourZ: got positive adjustment"); + } + d += adjustment; + if (d < min_down) { + d = min_down; + } + } + + if (d > max_up + 0.03 || d < min_down) { + d = 0; + } else { + if (d > max_up) { + d = max_up; + } + } + + if (path.role() == erExternalPerimeter && d > 0) { + // do not increase height of external perimeters as this may create an appearance of a seam + d = 0; + } + + if (std::abs(d) > EPSILON) { + was_contoured = true; + } + + Vec3d new_point = {p.x(), p.y(), d}; + + if (contoured_points.size() > 2) { + double dist = Linef3::distance_to_infinite_squared( + contoured_points[contoured_points.size() - 2], + contoured_points[contoured_points.size() - 1], + new_point); + if (dist < EPSILON) { + contoured_points[contoured_points.size() - 1] = new_point; + continue; + } + } + + contoured_points.push_back(new_point); + } + } + + if (!was_contoured) { + return false; + } + + Polyline3 polyline; + for (const Vec3d &point : contoured_points) { + polyline.append(Point3(scale_(point.x()), scale_(point.y()), scale_(point.z()))); + } + + path.polyline = std::move(polyline); + path.z_contoured = true; + return true; +} + +static void contour_extrusion_multipath(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionMultiPath &multipath) +{ + for (ExtrusionPath &path : multipath.paths) { + contour_extrusion_path(region, mesh, path); + } +} + +static void contour_extrusion_loop(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionLoop &loop) +{ + for (ExtrusionPath &path : loop.paths) { + contour_extrusion_path(region, mesh, path); + } +} + +static void contour_extrusion_entitiy_collection(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntityCollection &collection) { + for (ExtrusionEntity *entity : collection.entities) { + contour_extrusion_entity(region, mesh, entity); + } +} + +static void contour_extrusion_entity(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntity *extr) { + const ExtrusionPathSloped *sloped = dynamic_cast(extr); + if (sloped != nullptr) { + throw RuntimeError("ExtrusionPathSloped not implemented"); + return; + } + + ExtrusionMultiPath *multipath = dynamic_cast(extr); + if (multipath != nullptr) { + contour_extrusion_multipath(region, mesh, *multipath); + return; + } + + ExtrusionPath *path = dynamic_cast(extr); + if (path != nullptr) { + contour_extrusion_path(region, mesh, *path); + return; + } + + ExtrusionLoop *loop = dynamic_cast(extr); + if (loop != nullptr) { + contour_extrusion_loop(region, mesh, *loop); + return; + } + + const ExtrusionLoopSloped *loop_sloped = dynamic_cast(extr); + if (loop_sloped != nullptr) { + throw RuntimeError("ExtrusionLoopSloped not implemented"); + return; + } + + ExtrusionEntityCollection *collection = dynamic_cast(extr); + if (collection != nullptr) { + contour_extrusion_entitiy_collection(region, mesh, *collection); + return; + } + + throw RuntimeError("ContourZ: ExtrusionEntity type not implemented: " + std::string(typeid(*extr).name())); + return; +} + +static void handle_extrusion_collection(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntityCollection &collection, std::initializer_list roles) { + for (ExtrusionEntity *extr : collection.entities) { + // printf("handling extrusion collection %p %p\n", &collection, extr); + if (!contains(roles, extr->role())) { + continue; + } + + contour_extrusion_entity(region, mesh, extr); + } +} + +// static void find_point(ExtrusionPath &path, const std::string &path_info) { +// Points3 &points = path.polyline.points; + +// size_t i = 0; +// for (Points3::const_iterator it = points.begin(); it != points.end()-1; ++it) { +// if (it->x() == -883971 && it->y() == 979001) { +// std::cout << "FOUND POINT " << ExtrusionEntity::role_to_string(path.role()) << " at path " << path_info << "[" + std::to_string(i) + "]" << std::endl; +// } +// i++; +// } +// } + +// static void find_point(ExtrusionLoop &loop, const std::string &path_info) { +// size_t i = 0; +// for (ExtrusionPath &path : loop.paths) { +// find_point(path, path_info + "[" + std::to_string(i) + "]"); +// i++; +// } +// } + +// static void find_point(ExtrusionEntity &extr, const std::string &path); + +// static void find_point(ExtrusionEntityCollection &collection, const std::string &path) { +// size_t i = 0; +// for (ExtrusionEntity *extr : collection.entities) { +// find_point(*extr, path + "[" + std::to_string(i) + "]"); +// i++; +// } +// } + +// static void find_point(ExtrusionEntity &extr, const std::string &path_info) { +// const ExtrusionPathSloped *sloped = dynamic_cast(&extr); +// if (sloped != nullptr) { +// throw RuntimeError("ExtrusionPathSloped not implemented"); +// return; +// } + +// ExtrusionPath *path = dynamic_cast(&extr); +// if (path != nullptr) { +// find_point(*path, path_info + " as ExtrusionPath " + ExtrusionEntity::role_to_string(extr.role())); +// return; +// } + +// ExtrusionLoop *loop = dynamic_cast(&extr); +// if (loop != nullptr) { +// find_point(*loop, path_info + " as ExtrusionLoop " + ExtrusionEntity::role_to_string(extr.role())); +// return; +// } + +// const ExtrusionLoopSloped *loop_sloped = dynamic_cast(&extr); +// if (loop_sloped != nullptr) { +// throw RuntimeError("ExtrusionLoopSloped not implemented"); +// return; +// } + +// ExtrusionEntityCollection *collection = dynamic_cast(&extr); +// if (collection != nullptr) { +// find_point(*collection, path_info + " as ExtrusionEntityCollection " + ExtrusionEntity::role_to_string(extr.role())); +// return; +// } + +// throw RuntimeError("ContourZ: ExtrusionEntity type not implemented"); +// return; +// } + +void Layer::make_contour_z(const sla::IndexedMesh &mesh) +{ + // printf("make_contour_z() called\n"); + for (LayerRegion *region : this->regions()) { + // printf("processing layer region %p\n", region); + // find_point(region->fills, "fills"); + // find_point(region->perimeters, "perimeters"); + + handle_extrusion_collection(region, mesh, region->fills, {erTopSolidInfill, erIroning, erExternalPerimeter, erMixed}); + handle_extrusion_collection(region, mesh, region->perimeters, {erExternalPerimeter, erMixed}); + } +} +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 61d2cb9086..62cfbdd21a 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -17,12 +17,12 @@ static const double slope_inner_outer_wall_gap = 0.4; void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const { - this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection), retval); + this->_inflate_collection(intersection_pl(Polylines{ polyline.to_polyline() }, collection), retval); } void ExtrusionPath::subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const { - this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection), retval); + this->_inflate_collection(diff_pl(Polylines{ this->polyline.to_polyline() }, collection), retval); } void ExtrusionPath::clip_end(double distance) @@ -32,11 +32,17 @@ void ExtrusionPath::clip_end(double distance) void ExtrusionPath::simplify(double tolerance) { + if (this->z_contoured) { + return; + } this->polyline.simplify(tolerance); } void ExtrusionPath::simplify_by_fitting_arc(double tolerance) { + if (this->z_contoured) { + return; + } this->polyline.simplify_by_fitting_arc(tolerance); } @@ -45,15 +51,23 @@ double ExtrusionPath::length() const return this->polyline.length(); } +void ExtrusionPath::collect_points(Points &dst) const +{ + dst.reserve(dst.size() + this->polyline.points.size()); + for (const Point3 &point : this->polyline.points) { + dst.emplace_back(point.x(), point.y()); + } +} + void ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const { for (const Polyline &polyline : polylines) - collection->entities.emplace_back(new ExtrusionPath(polyline, *this)); + collection->entities.emplace_back(new ExtrusionPath(Polyline3(polyline), *this)); } void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { - polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon)); + polygons_append(out, offset(this->polyline.to_polyline(), float(scale_(this->width/2)) + scaled_epsilon)); } void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const @@ -64,7 +78,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale // SoftFever: TODO Mac trigger assersion errors // assert(! bridge || this->width == this->height); auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f); - polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); + polygons_append(out, offset(this->polyline.to_polyline(), 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); } void ExtrusionMultiPath::reverse() @@ -116,9 +130,10 @@ Polyline ExtrusionMultiPath::as_polyline() const len -= paths.size() - 1; assert(len > 0); out.points.reserve(len); - out.points.push_back(paths.front().polyline.points.front()); + out.points.push_back(paths.front().polyline.points.front().to_point()); for (size_t i_path = 0; i_path < paths.size(); ++ i_path) - out.points.insert(out.points.end(), paths[i_path].polyline.points.begin() + 1, paths[i_path].polyline.points.end()); + for (auto it = paths[i_path].polyline.points.begin() + 1; it != paths[i_path].polyline.points.end(); ++it) + out.points.push_back(it->to_point()); } return out; } @@ -149,7 +164,9 @@ Polygon ExtrusionLoop::polygon() const Polygon polygon; for (const ExtrusionPath &path : this->paths) { // for each polyline, append all points except the last one (because it coincides with the first one of the next polyline) - polygon.points.insert(polygon.points.end(), path.polyline.points.begin(), path.polyline.points.end()-1); + for (auto it = path.polyline.points.begin(); it != path.polyline.points.end() - 1; ++it) { + polygon.points.push_back(it->to_point()); + } } return polygon; } @@ -168,7 +185,7 @@ bool ExtrusionLoop::split_at_vertex(const Point &point, const double scaled_epsi if (int idx = path->polyline.find_point(point, scaled_epsilon); idx != -1) { if (this->paths.size() == 1) { // just change the order of points - Polyline p1, p2; + Polyline3 p1, p2; path->polyline.split_at_index(idx, &p1, &p2); if (p1.is_valid() && p2.is_valid()) { p2.append(std::move(p1)); @@ -178,7 +195,7 @@ bool ExtrusionLoop::split_at_vertex(const Point &point, const double scaled_epsi } else { // new paths list starts with the second half of current path ExtrusionPaths new_paths; - Polyline p1, p2; + Polyline3 p1, p2; path->polyline.split_at_index(idx, &p1, &p2); new_paths.reserve(this->paths.size() + 1); { @@ -218,16 +235,17 @@ ExtrusionLoop::ClosestPathPoint ExtrusionLoop::get_closest_path_and_point(const ClosestPathPoint best_non_overhang{0, 0}; double min2_non_overhang = std::numeric_limits::max(); for (const ExtrusionPath &path : this->paths) { - std::pair foot_pt_ = foot_pt(path.polyline.points, point); - double d2 = (foot_pt_.second - point).cast().squaredNorm(); + std::pair foot_pt_ = foot_pt(path.polyline.points, Point3(point)); + Point foot_pt_2d = Point(foot_pt_.second.x(), foot_pt_.second.y()); + double d2 = (foot_pt_2d - point).cast().squaredNorm(); if (d2 < min2) { - out.foot_pt = foot_pt_.second; + out.foot_pt = foot_pt_2d; out.path_idx = &path - &this->paths.front(); out.segment_idx = foot_pt_.first; min2 = d2; } if (prefer_non_overhang && !is_bridge(path.role()) && d2 < min2_non_overhang) { - best_non_overhang.foot_pt = foot_pt_.second; + best_non_overhang.foot_pt = foot_pt_2d; best_non_overhang.path_idx = &path - &this->paths.front(); best_non_overhang.segment_idx = foot_pt_.first; min2_non_overhang = d2; @@ -249,16 +267,18 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const // Snap p to start or end of segment_idx if closer than scaled_epsilon. { - const Point *p1 = this->paths[path_idx].polyline.points.data() + segment_idx; - const Point *p2 = p1; + const Point3 *p1 = this->paths[path_idx].polyline.points.data() + segment_idx; + const Point3 *p2 = p1; ++p2; - double d2_1 = (point - *p1).cast().squaredNorm(); - double d2_2 = (point - *p2).cast().squaredNorm(); + Point p1_2d = Point(p1->x(), p1->y()); + Point p2_2d = Point(p2->x(), p2->y()); + double d2_1 = (point - p1_2d).cast().squaredNorm(); + double d2_2 = (point - p2_2d).cast().squaredNorm(); const double thr2 = scaled_epsilon * scaled_epsilon; if (d2_1 < d2_2) { - if (d2_1 < thr2) p = *p1; + if (d2_1 < thr2) p = p1_2d; } else { - if (d2_2 < thr2) p = *p2; + if (d2_2 < thr2) p = p2_2d; } } @@ -411,16 +431,16 @@ ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths, : ExtrusionLoop(role) { // create slopes - const auto add_slop = [this, slope_max_segment_length, seam_gap](const ExtrusionPath &path, const Polyline &poly, double ratio_begin, double ratio_end) { + const auto add_slop = [this, slope_max_segment_length, seam_gap](const ExtrusionPath &path, const Polyline3 &poly, double ratio_begin, double ratio_end) { if (poly.empty()) { return; } // Ensure `slope_max_segment_length` - Polyline detailed_poly; + Polyline3 detailed_poly; { detailed_poly.append(poly.first_point()); // Recursively split the line into half until no longer than `slope_max_segment_length` - const std::function handle_line = [slope_max_segment_length, &detailed_poly, &handle_line](const Line &line) { + const std::function handle_line = [slope_max_segment_length, &detailed_poly, &handle_line](const Line3 &line) { if (line.length() <= slope_max_segment_length) { detailed_poly.append(line.b); } else { @@ -441,8 +461,8 @@ ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths, const auto seg_length = detailed_poly.length(); if (seg_length > seam_gap) { // Split the segment and remove the last `seam_gap` bit - const Polyline orig = detailed_poly; - Polyline tmp; + const Polyline3 orig = detailed_poly; + Polyline3 tmp; orig.split_at_length(seg_length - seam_gap, &detailed_poly, &tmp); ratio_end = lerp(ratio_begin, ratio_end, (seg_length - seam_gap) / seg_length); @@ -464,8 +484,8 @@ ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths, const double path_len = unscale_(path->length()); if (path_len > remaining_length) { // Split current path into slope and non-slope part - Polyline slope_path; - Polyline flat_path; + Polyline3 slope_path; + Polyline3 flat_path; path->polyline.split_at_length(scale_(remaining_length), &slope_path, &flat_path); add_slop(*path, slope_path, start_ratio, 1); @@ -631,4 +651,28 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role) return erNone; } +// ExtrusionPathContoured implementation +ExtrusionEntity *ExtrusionPathContoured::clone() const { + return new ExtrusionPathContoured(*this); +} + +ExtrusionEntity *ExtrusionPathContoured::clone_move() { + return new ExtrusionPathContoured(std::move(*this)); +} + +void ExtrusionPathContoured::simplify(double tolerance) { + // Do not simplify contoured paths + return; +} + +void ExtrusionPathContoured::simplify_by_fitting_arc(double tolerance) { + // Do not simplify contoured paths + return; +} + +void ExtrusionPathContoured::reverse() { + this->polyline.reverse(); + std::reverse(this->z_diffs.begin(), this->z_diffs.end()); +} + } diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index edd7839cc1..ce74d93e9e 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -116,8 +116,8 @@ public: virtual ExtrusionEntity* clone_move() = 0; virtual ~ExtrusionEntity() {} virtual void reverse() = 0; - virtual const Point& first_point() const = 0; - virtual const Point& last_point() const = 0; + virtual Point first_point() const = 0; + virtual Point last_point() const = 0; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0; @@ -150,13 +150,17 @@ typedef std::vector ExtrusionEntitiesPtr; class ExtrusionPath : public ExtrusionEntity { public: - Polyline polyline; + Polyline3 polyline; + double overhang_degree = 0; + int curve_degree = 0; // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. double mm3_per_mm; // Width of the extrusion, used for visualization purposes. float width; // Height of the extrusion, used for visualization purposes. float height; + double smooth_speed = 0; + bool z_contoured = false; ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1), m_role(erNone), m_no_extrusion(false) {} ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role), m_no_extrusion(false) {} @@ -164,36 +168,52 @@ public: ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline) + , overhang_degree(rhs.overhang_degree) + , curve_degree(rhs.curve_degree) , mm3_per_mm(rhs.mm3_per_mm) , width(rhs.width) , height(rhs.height) + , smooth_speed(rhs.smooth_speed) + , z_contoured(rhs.z_contoured) , m_can_reverse(rhs.m_can_reverse) , m_role(rhs.m_role) , m_no_extrusion(rhs.m_no_extrusion) {} ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)) + , overhang_degree(rhs.overhang_degree) + , curve_degree(rhs.curve_degree) , mm3_per_mm(rhs.mm3_per_mm) , width(rhs.width) , height(rhs.height) + , smooth_speed(rhs.smooth_speed) + , z_contoured(rhs.z_contoured) , m_can_reverse(rhs.m_can_reverse) , m_role(rhs.m_role) , m_no_extrusion(rhs.m_no_extrusion) {} - ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) + ExtrusionPath(const Polyline3 &polyline, const ExtrusionPath &rhs) : polyline(polyline) + , overhang_degree(rhs.overhang_degree) + , curve_degree(rhs.curve_degree) , mm3_per_mm(rhs.mm3_per_mm) , width(rhs.width) , height(rhs.height) + , smooth_speed(rhs.smooth_speed) + , z_contoured(rhs.z_contoured) , m_can_reverse(rhs.m_can_reverse) , m_role(rhs.m_role) , m_no_extrusion(rhs.m_no_extrusion) {} - ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) + ExtrusionPath(Polyline3 &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)) + , overhang_degree(rhs.overhang_degree) + , curve_degree(rhs.curve_degree) , mm3_per_mm(rhs.mm3_per_mm) , width(rhs.width) , height(rhs.height) + , smooth_speed(rhs.smooth_speed) + , z_contoured(rhs.z_contoured) , m_can_reverse(rhs.m_can_reverse) , m_role(rhs.m_role) , m_no_extrusion(rhs.m_no_extrusion) @@ -206,6 +226,10 @@ public: this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; + this->smooth_speed = rhs.smooth_speed; + this->z_contoured = rhs.z_contoured; + this->overhang_degree = rhs.overhang_degree; + this->curve_degree = rhs.curve_degree; this->polyline = rhs.polyline; return *this; } @@ -216,6 +240,10 @@ public: this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; + this->smooth_speed = rhs.smooth_speed; + this->z_contoured = rhs.z_contoured; + this->overhang_degree = rhs.overhang_degree; + this->curve_degree = rhs.curve_degree; this->polyline = std::move(rhs.polyline); return *this; } @@ -224,8 +252,10 @@ public: // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); } void reverse() override { this->polyline.reverse(); } - const Point& first_point() const override { return this->polyline.points.front(); } - const Point& last_point() const override { return this->polyline.points.back(); } + Point first_point() const override { return this->polyline.points.front().to_point(); } + Point3 first_point3() const { return this->polyline.points.front(); } + Point last_point() const override { return this->polyline.points.back().to_point(); } + Point3 last_point3() const { return this->polyline.points.back(); } size_t size() const { return this->polyline.size(); } bool empty() const { return this->polyline.empty(); } bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); } @@ -236,7 +266,7 @@ public: // Currently not used. void subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const; void clip_end(double distance); - void simplify(double tolerance); + virtual void simplify(double tolerance); double length() const override; ExtrusionRole role() const override { return m_role; } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. @@ -252,9 +282,10 @@ public: { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. double min_mm3_per_mm() const override { return this->mm3_per_mm; } - Polyline as_polyline() const override { return this->polyline; } - void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } - void collect_points(Points &dst) const override { append(dst, this->polyline.points); } + Polyline as_polyline() const override { return this->polyline.to_polyline(); } + void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline.to_polyline()); } + void collect_points(Points &dst) const override; + void collect_points3(Points3 &dst) const { append(dst, this->polyline.points); } double total_volume() const override { return mm3_per_mm * unscale(length()); } //BBS: add new simplifing method by fitting arc @@ -274,6 +305,23 @@ private: bool m_no_extrusion = false; }; +class ExtrusionPathContoured : public ExtrusionPath { +public: + std::vector z_diffs; + + ExtrusionPathContoured(Polyline3 &&polyline, const ExtrusionPath &rhs, std::vector &&z_diffs) + : ExtrusionPath(std::move(polyline), rhs), z_diffs(std::move(z_diffs)) + {} + + virtual ExtrusionEntity *clone() const override; + virtual ExtrusionEntity *clone_move() override; + + void simplify(double tolerance) override; + virtual void simplify_by_fitting_arc(double tolerance); + + void reverse() override; +}; + class ExtrusionPathSloped : public ExtrusionPath { public: @@ -292,10 +340,10 @@ public: ExtrusionPathSloped(ExtrusionPath&& rhs, const Slope& begin, const Slope& end) : ExtrusionPath(std::move(rhs)), slope_begin(begin), slope_end(end) {} - ExtrusionPathSloped(const Polyline& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end) + ExtrusionPathSloped(const Polyline3& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end) : ExtrusionPath(polyline, rhs), slope_begin(begin), slope_end(end) {} - ExtrusionPathSloped(Polyline&& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end) + ExtrusionPathSloped(Polyline3&& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end) : ExtrusionPath(std::move(polyline), rhs), slope_begin(begin), slope_end(end) {} @@ -354,8 +402,8 @@ public: // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); } void reverse() override; - const Point& first_point() const override { return this->paths.front().polyline.points.front(); } - const Point& last_point() const override { return this->paths.back().polyline.points.back(); } + Point first_point() const override { return this->paths.front().polyline.points.front().to_point(); } + Point last_point() const override { return this->paths.back().polyline.points.back().to_point(); } size_t size() const { return this->paths.size(); } bool empty() const { return this->paths.empty(); } double length() const override; @@ -379,7 +427,7 @@ public: size_t n = std::accumulate(paths.begin(), paths.end(), 0, [](const size_t n, const ExtrusionPath &p){ return n + p.polyline.size(); }); dst.reserve(dst.size() + n); for (const ExtrusionPath &p : this->paths) - append(dst, p.polyline.points); + append(dst, to_points(p.polyline.points)); } double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } @@ -410,8 +458,8 @@ public: bool is_clockwise() { return this->polygon().is_clockwise(); } bool is_counter_clockwise() { return this->polygon().is_counter_clockwise(); } void reverse() override; - const Point& first_point() const override { return this->paths.front().polyline.points.front(); } - const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } + Point first_point() const override { return this->paths.front().polyline.points.front().to_point(); } + Point last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back().to_point()); return this->first_point(); } Polygon polygon() const; double length() const override; bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled(0.001)); @@ -449,7 +497,7 @@ public: size_t n = std::accumulate(paths.begin(), paths.end(), 0, [](const size_t n, const ExtrusionPath &p){ return n + p.polyline.size(); }); dst.reserve(dst.size() + n); for (const ExtrusionPath &p : this->paths) - append(dst, p.polyline.points); + append(dst, to_points(p.polyline.points)); } double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } // check if the loop is smooth, angle_threshold is in radians, default is 10 degrees @@ -495,7 +543,7 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, Ex for (Polyline &polyline : polylines) if (polyline.is_valid()) { dst.emplace_back(role, mm3_per_mm, width, height); - dst.back().polyline = polyline; + dst.back().polyline = Polyline3(polyline); } } @@ -505,7 +553,7 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, E for (Polyline &polyline : polylines) if (polyline.is_valid()) { dst.emplace_back(role, mm3_per_mm, width, height); - dst.back().polyline = std::move(polyline); + dst.back().polyline = Polyline3(std::move(polyline)); } polylines.clear(); } @@ -515,7 +563,7 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polyline &&polyline, Ext dst.reserve(dst.size() + 1); if (polyline.is_valid()) { dst.emplace_back(role, mm3_per_mm, width, height); - dst.back().polyline = std::move(polyline); + dst.back().polyline = Polyline3(std::move(polyline)); } } @@ -526,7 +574,7 @@ inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines if (polyline.is_valid()) { ExtrusionPath *extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height); dst.push_back(extrusion_path); - extrusion_path->polyline = polyline; + extrusion_path->polyline = Polyline3(polyline); } } @@ -537,7 +585,7 @@ inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines if (polyline.is_valid()) { ExtrusionPath *extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height); dst.push_back(extrusion_path); - extrusion_path->polyline = std::move(polyline); + extrusion_path->polyline = Polyline3(std::move(polyline)); } polylines.clear(); } @@ -557,7 +605,7 @@ inline void extrusion_entities_append_paths_with_wipe(ExtrusionEntitiesPtr &dst, Point temp = polyline.first_point() - last_end_point; if (Vec2d(temp.x(), temp.y()).norm() <= 3 * scaled(width)) { multi_path->paths.emplace_back(role, mm3_per_mm, width, height, true); - multi_path->paths.back().polyline = std::move(Polyline(last_end_point, polyline.first_point())); + multi_path->paths.back().polyline = Polyline3(Polyline(last_end_point, polyline.first_point())); } else { dst.push_back(multi_path); multi_path = new ExtrusionMultiPath(); @@ -565,9 +613,9 @@ inline void extrusion_entities_append_paths_with_wipe(ExtrusionEntitiesPtr &dst, } multi_path->paths.emplace_back(role, mm3_per_mm, width, height); - multi_path->paths.back().polyline = std::move(polyline); + multi_path->paths.back().polyline = Polyline3(std::move(polyline)); last_end_point_valid = true; - last_end_point = multi_path->paths.back().polyline.last_point(); + last_end_point = multi_path->paths.back().polyline.last_point().to_point(); } } if (!multi_path->empty()) @@ -582,7 +630,9 @@ inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons for (Polygon &poly : loops) { if (poly.is_valid()) { ExtrusionPath path(role, mm3_per_mm, width, height); - path.polyline.points = std::move(poly.points); + path.polyline.points.reserve(poly.points.size() + 1); + for (const Point &pt : poly.points) + path.polyline.points.emplace_back(Point3(pt, 0)); path.polyline.points.push_back(path.polyline.points.front()); dst.emplace_back(new ExtrusionLoop(std::move(path))); } @@ -597,11 +647,11 @@ inline void extrusion_entities_append_loops_and_paths(ExtrusionEntitiesPtr &dst, if (polyline.is_valid()) { if (polyline.is_closed()) { ExtrusionPath extrusion_path(role, mm3_per_mm, width, height); - extrusion_path.polyline = std::move(polyline); + extrusion_path.polyline = Polyline3(std::move(polyline)); dst.emplace_back(new ExtrusionLoop(std::move(extrusion_path))); } else { ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); - extrusion_path->polyline = std::move(polyline); + extrusion_path->polyline = Polyline3(std::move(polyline)); dst.emplace_back(extrusion_path); } } diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 613d531db0..35c9e24743 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -118,8 +118,8 @@ public: ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const { return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); } void reverse() override; - const Point& first_point() const override { return this->entities.front()->first_point(); } - const Point& last_point() const override { return this->entities.back()->last_point(); } + Point first_point() const override { return this->entities.front()->first_point(); } + Point last_point() const override { return this->entities.back()->last_point(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; diff --git a/src/libslic3r/ExtrusionSimulator.cpp b/src/libslic3r/ExtrusionSimulator.cpp index 6b1f76abea..4bf54edea9 100644 --- a/src/libslic3r/ExtrusionSimulator.cpp +++ b/src/libslic3r/ExtrusionSimulator.cpp @@ -962,7 +962,7 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const w = scale_(path.mm3_per_mm / path.height) * scalex; // printf("scalex: %f, scaley: %f\n", scalex, scaley); // printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y); - for (Points::const_iterator it = path.polyline.points.begin(); it != path.polyline.points.end(); ++ it) { + for (Points3::const_iterator it = path.polyline.points.begin(); it != path.polyline.points.end(); ++ it) { // printf("point %d,%d\n", it->x+shift.x(), it->y+shift.y); ExtrusionPoint ept; ept.center = V2f(float((*it)(0)+shift.x()-bbox.min.x()) * scalex, float((*it)(1)+shift.y()-bbox.min.y()) * scaley); diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index a44fa46606..79f835816a 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -1215,6 +1215,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); f->layer_id = this->id(); + f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->angle = surface_fill.params.angle; f->fixed_angle = surface_fill.params.fixed_angle; @@ -1408,6 +1409,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers. + f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->angle = surface_fill.params.angle; f->fixed_angle = surface_fill.params.fixed_angle; @@ -1580,6 +1582,7 @@ void Layer::make_ironing() std::unique_ptr f = std::unique_ptr(Fill::new_from_type(f_pattern)); f->set_bounding_box(this->object()->bounding_box()); f->layer_id = this->id(); + f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->overlap = 0; for (size_t i = 0; i < by_extruder.size();) { @@ -1592,6 +1595,7 @@ void Layer::make_ironing() f = std::unique_ptr(Fill::new_from_type(f_pattern)); f->set_bounding_box(this->object()->bounding_box()); f->layer_id = this->id(); + f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->overlap = 0; } diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index eff83efe3e..9358667982 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -308,7 +308,9 @@ std::pair Fill::_infill_direction(const Surface *surface) const } else if (this->layer_id != size_t(-1) && !fixed_angle) { // alternate fill direction //Orca: Do not alternate direction if Fill.fixed_angle is true - out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers); + if (!this->dont_alternate_fill_direction) { + out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers); + } } else { // printf("Layer_ID undefined!\n"); } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 010dff8a08..47195c850d 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -141,6 +141,7 @@ public: // BBS: all no overlap expolygons in same layer ExPolygons no_overlap_expolygons; + bool dont_alternate_fill_direction = false; static float infill_anchor; static float infill_anchor_max; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 9b82b60b06..a5ee9942fd 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -22,10 +22,12 @@ #include "Time.hpp" #include "GCode/ExtrusionProcessor.hpp" #include +#include #include #include #include #include +#include #include #include #include @@ -5289,12 +5291,12 @@ void GCode::set_extruders(const std::vector &extruder_ids) void GCode::set_origin(const Vec2d &pointf) { // if origin increases (goes towards right), last_pos decreases because it goes towards left - const Point translate( + const Point3 translate( scale_(m_origin(0) - pointf(0)), scale_(m_origin(1) - pointf(1)) ); m_last_pos += translate; - m_wipe.path.translate(translate); + m_wipe.path.translate(translate.to_point()); m_origin = pointf; } @@ -5371,11 +5373,11 @@ static std::unique_ptr calculate_layer_edge_grid(const Layer& la return out; } -std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters, const Point* start_point) +std::string GCode::extrude_loop(const ExtrusionLoop &loop_ref, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters, const Point* start_point) { - // get a copy; don't modify the orientation of the original loop object otherwise // next copies (if any) would not detect the correct orientation + ExtrusionLoop loop = loop_ref; bool is_hole = (loop.loop_role() & elrHole) == elrHole; @@ -5447,13 +5449,14 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou const double nozzle_diam = nozzle_diameter; // note: previous & next are inverted to extrude "in the opposite direction, and we are "rewinding" - Point previous_point = paths.front().polyline.points[1]; - Point current_point = paths.front().polyline.points.front(); - Point next_point = paths.back().polyline.points.back(); + Point previous_point = Point(paths.front().polyline.points[1].x(), paths.front().polyline.points[1].y()); + Point current_point = Point(paths.front().polyline.points.front().x(), paths.front().polyline.points.front().y()); + Point next_point = Point(paths.back().polyline.points.back().x(), paths.back().polyline.points.back().y()); // can happen if seam_gap is null if (next_point == current_point) { - next_point = paths.back().polyline.points[paths.back().polyline.points.size() - 2]; + const Point3 &p3 = paths.back().polyline.points[paths.back().polyline.points.size() - 2]; + next_point = Point(p3.x(), p3.y()); } Point a = next_point; // second point @@ -5500,7 +5503,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // inside the model if(discoveredTouchingLines > 1){ // use extrude instead of travel_to_xy to trigger the unretract - ExtrusionPath fake_path_wipe(Polyline{pt, current_point}, paths.front()); + ExtrusionPath fake_path_wipe(Polyline3(Points3{Point3(pt), Point3(current_point)}), paths.front()); fake_path_wipe.set_force_no_extrusion(true); fake_path_wipe.mm3_per_mm = 0; //fake_path_wipe.set_extrusion_role(erExternalPerimeter); @@ -5594,10 +5597,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou for (ExtrusionPath &path : paths) { //BBS: Don't need to save duplicated point into wipe path if (!m_wipe.path.empty() && !path.empty() && - m_wipe.path.last_point() == path.first_point()) - m_wipe.path.append(path.polyline.points.begin() + 1, path.polyline.points.end()); - else - m_wipe.path.append(path.polyline); // TODO: don't limit wipe to last path + m_wipe.path.last_point() == Point(path.first_point().x(), path.first_point().y())) { + // Convert Points3 to Points + for (auto it = path.polyline.points.begin() + 1; it != path.polyline.points.end(); ++it) + m_wipe.path.append(Point(it->x(), it->y())); + } else + m_wipe.path.append(path.polyline.to_polyline()); // TODO: don't limit wipe to last path } } @@ -5607,8 +5612,10 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // the side depends on the original winding order of the polygon (inwards for contours, outwards for holes) //FIXME improve the algorithm in case the loop is tiny. //FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query). - Point a = paths.front().polyline.points[1]; // second point - Point b = *(paths.back().polyline.points.end()-3); // second to last point + const Point3 &a3 = paths.front().polyline.points[1]; // second point + Point a = Point(a3.x(), a3.y()); + const Point3 &b3 = *(paths.back().polyline.points.end()-3); // second to last point + Point b = Point(b3.x(), b3.y()); if (is_hole == loop.is_counter_clockwise()) { // swap points Point c = a; a = b; b = c; @@ -5622,8 +5629,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // create the destination point along the first segment and rotate it // we make sure we don't exceed the segment length because we don't know // the rotation of the second segment so we might cross the object boundary - Vec2d p1 = paths.front().polyline.points.front().cast(); - Vec2d p2 = paths.front().polyline.points[1].cast(); + Vec2d p1 = paths.front().polyline.points.front().cast().head<2>(); + Vec2d p2 = paths.front().polyline.points[1].cast().head<2>(); Vec2d v = p2 - p1; double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); double l2 = v.squaredNorm(); @@ -5635,7 +5642,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou if (nd * nd < l2) pt = (p1 + threshold * v * (nd / sqrt(l2))).cast(); //Point pt = ((nd * nd >= l2) ? (p1+v*0.4): (p1 + 0.2 * v * (nd / sqrt(l2)))).cast(); - pt.rotate(angle, paths.front().polyline.points.front()); + const Point3 ¢er3 = paths.front().polyline.points.front(); + pt.rotate(angle, Point(center3.x(), center3.y())); // generate the travel move gcode += m_writer.extrude_to_xy(this->point_to_gcode(pt), 0,"move inwards before travel",true); } @@ -5643,11 +5651,11 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou return gcode; } -std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string description, double speed) +std::string GCode::extrude_multi_path(const ExtrusionMultiPath &multipath, std::string description, double speed) { // extrude along the path std::string gcode; - + //Orca: calculate multipath average mm3_per_mm value over the length of the path. //This is used for adaptive PA m_multi_flow_segment_path_pa_set = false; // always emit PA on the first path of the multi-path @@ -5664,8 +5672,8 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string if (total_multipath_length > 0.0) m_multi_flow_segment_path_average_mm3_per_mm = weighted_sum_mm3_per_mm / total_multipath_length; // Orca: end of multipath average mm3_per_mm value calculation - - for (ExtrusionPath path : multipath.paths){ + + for (const ExtrusionPath &path : multipath.paths){ gcode += this->_extrude(path, description, speed); // Orca: Adaptive PA - dont adapt PA after the first pultipath extrusion is completed // as we have already set the PA value to the average flow over the totality of the path @@ -5676,13 +5684,15 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string // BBS if (m_wipe.enable && FILAMENT_CONFIG(wipe)) { m_wipe.path = Polyline(); - for (ExtrusionPath &path : multipath.paths) { + for (const ExtrusionPath &path : multipath.paths) { //BBS: Don't need to save duplicated point into wipe path if (!m_wipe.path.empty() && !path.empty() && - m_wipe.path.last_point() == path.first_point()) - m_wipe.path.append(path.polyline.points.begin() + 1, path.polyline.points.end()); - else - m_wipe.path.append(path.polyline); // TODO: don't limit wipe to last path + m_wipe.path.last_point() == Point(path.first_point().x(), path.first_point().y())) { + // Convert Points3 to Points + for (auto it = path.polyline.points.begin() + 1; it != path.polyline.points.end(); ++it) + m_wipe.path.append(Point(it->x(), it->y())); + } else + m_wipe.path.append(path.polyline.to_polyline()); // TODO: don't limit wipe to last path } m_wipe.path.reverse(); } @@ -5703,7 +5713,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des return ""; } -std::string GCode::extrude_path(ExtrusionPath path, std::string description, double speed) +std::string GCode::extrude_path(const ExtrusionPath &path, std::string description, double speed) { // Orca: Reset average multipath flow as this is a single line, single extrude volumetric speed path m_multi_flow_segment_path_pa_set = false; @@ -5711,17 +5721,17 @@ std::string GCode::extrude_path(ExtrusionPath path, std::string description, dou // description += ExtrusionEntity::role_to_string(path.role()); std::string gcode = this->_extrude(path, description, speed); if (m_wipe.enable && FILAMENT_CONFIG(wipe)) { - m_wipe.path = path.polyline; + m_wipe.path = path.polyline.to_polyline(); if (is_tree(this->config().support_type) && (path.role() == erSupportMaterial || path.role() == erSupportMaterialInterface || path.role() == erSupportTransition)) { if ((m_wipe.path.first_point() - m_wipe.path.last_point()).cast().norm() > scale_(0.2)) { double min_dist = scale_(0.2); int i = 0; for (; i < path.polyline.points.size(); i++) { - double dist = (path.polyline.points[i] - path.last_point()).cast().norm(); + double dist = (path.polyline.points[i] - path.last_point3()).cast().norm(); if (dist < min_dist) min_dist = dist; if (min_dist < scale_(0.2) && dist > min_dist) break; } - m_wipe.path = Polyline(Points(path.polyline.points.begin() + i - 1, path.polyline.points.end())); + m_wipe.path = Polyline3(Points3(path.polyline.points.begin() + i - 1, path.polyline.points.end())).to_polyline(); } } else m_wipe.path.reverse(); @@ -5764,11 +5774,11 @@ std::string GCode::extrude_infill(const Print &print, const std::vector(fill); if (eec) { - for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) + for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos.to_point()).entities) gcode += this->extrude_entity(*ee, extrusion_name); } else gcode += this->extrude_entity(*fill, extrusion_name); @@ -5799,7 +5809,7 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill if (extrusions.empty()) return gcode; - chain_and_reorder_extrusion_entities(extrusions, &m_last_pos); + chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point()); const double support_speed = m_config.support_speed.value; const double support_interface_speed = m_config.get_abs_value("support_interface_speed"); @@ -5965,13 +5975,23 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // Move to first point of extrusion path // path is 2D. But in slope lift case, lift z is done in travel_to function. // Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance - if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z || slope_need_z_travel) { + Point3 first_point = path.first_point3(); + if (!m_last_pos_defined || m_last_pos != first_point || m_need_change_layer_lift_z || slope_need_z_travel) { const bool _last_pos_undefined = !m_last_pos_defined; + double z = DBL_MAX; + if (sloped != nullptr) { + z = get_sloped_z(sloped->slope_begin.z_ratio); + } else if ((!m_last_pos_defined && first_point.z() != 0) || m_last_pos.z() != first_point.z()) { + z = m_nominal_z + unscale_(first_point.z()); + if (z < 0.1) { + throw RuntimeError("GCode: very low z"); + } + } gcode += this->travel_to( path.first_point(), path.role(), - "move to first " + description + " point", - sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio) + "move to first " + description + " point; size " + std::to_string(path.polyline.size()), + z ); m_need_change_layer_lift_z = false; // Orca: ensure Z matches planned layer height @@ -6536,10 +6556,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } // BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode or we are doing sloped extrusion // Attention: G2 and G3 is not supported in spiral_mode mode - if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr) { + if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr || path.z_contoured) { double path_length = 0.; double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR; - for (const Line& line : path.polyline.lines()) { + for (const Line3& line : path.polyline.lines()) { std::string tempDescription = description; const double line_length = line.length() * SCALING_FACTOR; if (line_length < EPSILON) @@ -6554,16 +6574,37 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length); } } - if (sloped == nullptr) { + if (path.z_contoured) { + // ZAA: Z anti-aliased extrusion with variable Z per point + Vec2d dest2d = this->point_to_gcode(line.b.to_point()); + coordf_t z_diff = unscale_(line.b.z()); + + double extrusion_ratio = 1; + if (path.role() != erIroning) { + extrusion_ratio = (path.height + z_diff) / path.height; + } + + double e = dE * extrusion_ratio; + + double z = m_nominal_z + z_diff; + if (z < 0.1) { + throw RuntimeError("GCode: very low z"); + } + gcode += m_writer.extrude_to_xyz( + Vec3d(dest2d.x(), dest2d.y(), z), + e, + tempDescription + "; z_diff " + std::to_string(z_diff) + " " + ExtrusionEntity::role_to_string(path.role()) + "; eratio " + std::to_string(extrusion_ratio)); + + } else if (sloped == nullptr) { // Normal extrusion gcode += m_writer.extrude_to_xy( - this->point_to_gcode(line.b), + this->point_to_gcode(line.b.to_point()), dE, GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion()); } else { // Sloped extrusion const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length); - Vec2d dest2d = this->point_to_gcode(line.b); + Vec2d dest2d = this->point_to_gcode(line.b.to_point()); Vec3d dest3d(dest2d(0), dest2d(1), get_sloped_z(z_ratio)); gcode += m_writer.extrude_to_xyz( dest3d, @@ -6582,7 +6623,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, size_t end_index = fitting_result[fitting_index].end_point_index; for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) { tempDescription = description; - const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]); + const Line line = Line(path.polyline.points[point_index - 1].to_point(), path.polyline.points[point_index].to_point()); const double line_length = line.length() * SCALING_FACTOR; if (line_length < EPSILON) continue; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index b2047bd3aa..d47e8f7c73 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -221,7 +221,7 @@ public: const Vec2d& origin() const { return m_origin; } void set_origin(const Vec2d &pointf); void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); } - const Point& last_pos() const { return m_last_pos; } + Point last_pos() const { return m_last_pos.to_point(); } Vec2d point_to_gcode(const Point &point) const; Point gcode_to_point(const Vec2d &point) const; Vec2d point_to_gcode_quantized(const Point& point) const; @@ -381,7 +381,8 @@ private: void check_placeholder_parser_failed(); size_t get_extruder_id(unsigned int filament_id) const; - void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; } + void set_last_pos(const Point &pos) { m_last_pos = Point3(pos, 0); m_last_pos_defined = true; } + void set_last_pos(const Point3 &pos) { m_last_pos = pos; m_last_pos_defined = true; } bool last_pos_defined() const { return m_last_pos_defined; } void set_extruders(const std::vector &extruder_ids); std::string preamble(); @@ -392,9 +393,9 @@ private: std::string extrude_entity(const ExtrusionEntity &entity, std::string description = "", double speed = -1., const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr()); // Orca: pass the complete collection of region perimeters to the extrude loop to check whether the wipe before external loop // should be executed - std::string extrude_loop(ExtrusionLoop loop, std::string description, double speed = -1., const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr(), const Point* start_point = nullptr); - std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.); - std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.); + std::string extrude_loop(const ExtrusionLoop &loop, std::string description, double speed = -1., const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr(), const Point* start_point = nullptr); + std::string extrude_multi_path(const ExtrusionMultiPath &multipath, std::string description = "", double speed = -1.); + std::string extrude_path(const ExtrusionPath &path, std::string description = "", double speed = -1.); // Orca: Adaptive PA variables // Used for adaptive PA when extruding paths with multiple, varying flow segments. @@ -582,7 +583,7 @@ private: std::map> m_placeholder_error_messages; #endif - Point m_last_pos; + Point3 m_last_pos; bool m_last_pos_defined; std::unique_ptr m_cooling_buffer; diff --git a/src/libslic3r/GCode/ConflictChecker.hpp b/src/libslic3r/GCode/ConflictChecker.hpp index 30a83676e8..a10b969431 100644 --- a/src/libslic3r/GCode/ConflictChecker.hpp +++ b/src/libslic3r/GCode/ConflictChecker.hpp @@ -87,7 +87,7 @@ public: for (int i = b; i < e; ++i) { for (const ExtrusionPath &path : _piles[i].paths) { if (path.is_force_no_extrusion() == false) { - Polyline check_polyline = path.polyline; + Polyline check_polyline = path.polyline.to_polyline(); check_polyline.translate(_offset); Lines tmpLines = check_polyline.lines(); for (const Line &line : tmpLines) { lines.emplace_back(line, _id, path.role()); } diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 62a01db11e..dad88a7485 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -81,7 +81,19 @@ std::vector estimate_points_properties(const POINTS if (input_points.empty()) return {}; float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; - auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; + auto maybe_unscale = [](const P &p) -> Vec2d { + if constexpr (P::RowsAtCompileTime == 3) { + // 3D point - extract XY only + if constexpr (SCALED_INPUT) { + return unscaled(p).template head<2>(); + } else { + return p.template head<2>().template cast(); + } + } else { + // 2D point - use as is + return SCALED_INPUT ? unscaled(p) : p.template cast(); + } + }; std::vector points; points.reserve(input_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1)); diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index af54f46a1f..4a65ae5ae4 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -30,7 +30,7 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path) { - BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))); + BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline.to_polyline(), coord_t(scale_(0.5 * extrusion_path.width))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); @@ -44,7 +44,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio { BoundingBox bbox; for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) - bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)))); + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline.to_polyline(), coord_t(scale_(0.5 * extrusion_path.width)))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); @@ -58,7 +58,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext { BoundingBox bbox; for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) - bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)))); + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline.to_polyline(), coord_t(scale_(0.5 * extrusion_path.width)))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index d75f0c35fc..12f19f6fa8 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1514,7 +1514,8 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, current.path_idx = next_idx_modulo(current.path_idx, loop.paths.size()); current.segment_idx = 0; } - current.foot_pt = loop.paths[current.path_idx].polyline.points[current.segment_idx]; + const Point3 &p3 = loop.paths[current.path_idx].polyline.points[current.segment_idx]; + current.foot_pt = Point(p3.x(), p3.y()); return current; }; @@ -1527,7 +1528,8 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, size_t closest_perimeter_point_index = 0; { // local space for the closest_perimeter_point_index Perimeter *closest_perimeter = nullptr; - ExtrusionLoop::ClosestPathPoint closest_point{0,0,loop.paths[0].polyline.points[0]}; + const Point3 &init_p3 = loop.paths[0].polyline.points[0]; + ExtrusionLoop::ClosestPathPoint closest_point{0,0,Point(init_p3.x(), init_p3.y())}; size_t points_count = std::accumulate(loop.paths.begin(), loop.paths.end(), 0, [](size_t acc,const ExtrusionPath& p) { return acc + p.polyline.points.size(); }); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 4e9fdad059..f1acad9ba7 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -26,6 +26,10 @@ namespace FillLightning { class Generator; }; +namespace sla { + class IndexedMesh; +}; + class LayerRegion { public: @@ -191,6 +195,7 @@ public: FillAdaptive::Octree *support_fill_octree, FillLightning::Generator* lightning_generator) const; void make_ironing(); + void make_contour_z(const sla::IndexedMesh &mesh); void export_region_slices_to_svg(const char *path) const; void export_region_fill_surfaces_to_svg(const char *path) const; diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 028ea2aafa..28423b0dfc 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -224,17 +224,23 @@ using CurledLines = std::vector; class Line3 { public: - Line3() : a(Vec3crd::Zero()), b(Vec3crd::Zero()) {} - Line3(const Vec3crd& _a, const Vec3crd& _b) : a(_a), b(_b) {} + Line3() : a(Point3()), b(Point3()) {} + Line3(const Point3& _a, const Point3& _b) : a(_a), b(_b) {} + // Backward compatibility with Vec3crd + Line3(const Vec3crd& _a, const Vec3crd& _b) : a(Point3(_a)), b(Point3(_b)) {} double length() const { return (this->a - this->b).cast().norm(); } - Vec3crd vector() const { return this->b - this->a; } + Point3 vector() const { Vec3crd v = this->b - this->a; return Point3(v.x(), v.y(), v.z()); } + Point3 midpoint() const { return Point3((this->a.x() + this->b.x()) / 2, (this->a.y() + this->b.y()) / 2, (this->a.z() + this->b.z()) / 2); } - Vec3crd a; - Vec3crd b; + // Convert to 2D line by dropping Z coordinate + Line to_line() const { return Line(this->a.to_point(), this->b.to_point()); } + + Point3 a; + Point3 b; static const constexpr int Dim = 3; - using Scalar = Vec3crd::Scalar; + using Scalar = coord_t; }; class Linef @@ -243,6 +249,10 @@ public: Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {} Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {} + Vec2d vector() const { return this->b - this->a; } + Vec2d unit_vector() const { return (length() == 0.0) ? Vec2d::Zero() : vector().normalized(); } + double length() const { return vector().norm(); } + Vec2d a; Vec2d b; @@ -263,6 +273,32 @@ public: Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); } double length() const { return vector().norm(); } + double distance_to_infinite_squared(const Vec3d &point, Vec3d *closest_point) const { + const Vec3d v = this->b - this->a; + const Vec3d va = point - this->a; + const double l2 = v.squaredNorm(); + if (l2 == 0.) { + // a == b case + *closest_point = this->a; + return va.squaredNorm(); + } + // Consider the line extending the segment, parameterized as a + t (b - a). + // Find parameter value t of the projection of point onto the line. + const double t = va.dot(v) / l2; + *closest_point = this->a + t * v; + return (point - *closest_point).squaredNorm(); + } + + double distance_to_infinite_squared(const Vec3d &point) const { + Vec3d nearest_point; + return distance_to_infinite_squared(point, &nearest_point); + } + + static inline double distance_to_infinite_squared(const Vec3d &point, const Vec3d &a, const Vec3d &b) { + Linef3 line{a, b}; + return line.distance_to_infinite_squared(point); + } + Vec3d a; Vec3d b; diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index fbf898234e..cb9c67f0f9 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -471,6 +471,51 @@ bool MultiPoint3::remove_duplicate_points() return false; } +// Douglas-Peucker simplification for 3D points +Points3 MultiPoint3::_douglas_peucker(const Points3 &points, double tolerance) +{ + if (points.size() <= 2) return points; + + // Find the point with maximum distance from line segment + double max_dist = 0; + size_t max_idx = 0; + const Point3 &first = points.front(); + const Point3 &last = points.back(); + Line3 line(first, last); + + for (size_t i = 1; i < points.size() - 1; ++i) { + // Calculate perpendicular distance to line segment + Point3 proj = points[i].projection_onto(line); + double dist = points[i].distance_to(proj); + if (dist > max_dist) { + max_dist = dist; + max_idx = i; + } + } + + // If max distance is greater than tolerance, recursively simplify + if (max_dist > tolerance) { + // Recursive call for first part + Points3 left_points(points.begin(), points.begin() + max_idx + 1); + Points3 left_result = _douglas_peucker(left_points, tolerance); + + // Recursive call for second part + Points3 right_points(points.begin() + max_idx, points.end()); + Points3 right_result = _douglas_peucker(right_points, tolerance); + + // Concatenate results (avoiding duplicate middle point) + Points3 result = left_result; + result.insert(result.end(), right_result.begin() + 1, right_result.end()); + return result; + } else { + // All points between first and last can be removed + Points3 result; + result.push_back(first); + result.push_back(last); + return result; + } +} + BoundingBox get_extents(const MultiPoint &mp) { return BoundingBox(mp.points); @@ -514,4 +559,78 @@ void MultiPoint::symmetric_y(const coord_t &x_axis) } } +// MultiPoint3 implementations +void MultiPoint3::rotate(double cos_angle, double sin_angle) +{ + for (Point3 &pt : this->points) { + double cur_x = double(pt(0)); + double cur_y = double(pt(1)); + pt(0) = coord_t(round(cos_angle * cur_x - sin_angle * cur_y)); + pt(1) = coord_t(round(cos_angle * cur_y + sin_angle * cur_x)); + // Keep Z unchanged + } +} + +void MultiPoint3::rotate(double angle, const Point3 ¢er) +{ + double s = sin(angle); + double c = cos(angle); + for (Point3 &pt : points) { + Vec3crd v(pt - center); + pt(0) = (coord_t)round(double(center(0)) + c * v[0] - s * v[1]); + pt(1) = (coord_t)round(double(center(1)) + c * v[1] + s * v[0]); + // Keep Z unchanged from original point + } +} + +int MultiPoint3::find_point(const Point &point) const +{ + for (const Point3 &pt : this->points) + if (pt.to_point() == point) + return int(&pt - &this->points.front()); + return -1; // not found +} + +int MultiPoint3::find_point(const Point &point, double scaled_epsilon) const +{ + if (scaled_epsilon == 0) return this->find_point(point); + + auto dist2_min = std::numeric_limits::max(); + auto eps2 = scaled_epsilon * scaled_epsilon; + int idx_min = -1; + for (const Point3 &pt : this->points) { + double d2 = (pt.to_point() - point).cast().squaredNorm(); + if (d2 < dist2_min) { + idx_min = int(&pt - &this->points.front()); + dist2_min = d2; + } + } + return (dist2_min < eps2) ? idx_min : -1; +} + +int MultiPoint3::find_point(const Point3 &point) const +{ + for (const Point3 &pt : this->points) + if (pt == point) + return int(&pt - &this->points.front()); + return -1; // not found +} + +int MultiPoint3::find_point(const Point3 &point, double scaled_epsilon) const +{ + if (scaled_epsilon == 0) return this->find_point(point); + + auto dist2_min = std::numeric_limits::max(); + auto eps2 = scaled_epsilon * scaled_epsilon; + int idx_min = -1; + for (const Point3 &pt : this->points) { + double d2 = (pt - point).cast().squaredNorm(); + if (d2 < dist2_min) { + idx_min = int(&pt - &this->points.front()); + dist2_min = d2; + } + } + return (dist2_min < eps2) ? idx_min : -1; +} + } diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 0dfb98f4b4..44d1e86cf1 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -119,18 +119,46 @@ class MultiPoint3 public: Points3 points; - void append(const Vec3crd& point) { this->points.push_back(point); } + void append(const Point3& point) { this->points.push_back(point); } + void append(const Vec3crd& point) { this->points.push_back(Point3(point)); } void translate(double x, double y); void translate(const Point& vector); + void reverse() { std::reverse(this->points.begin(), this->points.end()); } + void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } + void rotate(double cos_angle, double sin_angle); + void rotate(double angle, const Point3 ¢er); + + Point3& first_point() { return this->points.front(); } + Point3& last_point() { return this->points.back(); } + const Point3& first_point() const { return this->points.front(); } + const Point3& last_point() const { return this->points.back(); } + size_t size() const { return this->points.size(); } + bool empty() const { return this->points.empty(); } + void clear() { this->points.clear(); } + + auto begin() { return this->points.begin(); } + auto end() { return this->points.end(); } + auto begin() const { return this->points.begin(); } + auto end() const { return this->points.end(); } + virtual Lines3 lines() const = 0; double length() const; bool is_valid() const { return this->points.size() >= 2; } BoundingBox3 bounding_box() const; + // Find a point in the points array + int find_point(const Point &point) const; + int find_point(const Point &point, const double scaled_epsilon) const; + int find_point(const Point3 &point) const; + int find_point(const Point3 &point, const double scaled_epsilon) const; + // Remove exact duplicates, return true if any duplicate has been removed. bool remove_duplicate_points(); + + // Douglas-Peucker simplification + static Points3 _douglas_peucker(const Points3 &points, double tolerance); }; extern BoundingBox get_extents(const MultiPoint &mp); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index cc055c9d8a..b7cb3e34ef 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -206,7 +206,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime // Reapply the nearest point search for starting point. // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. if(paths.empty()) continue; - chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); + Point start_pt = Point(paths.front().first_point().x(), paths.front().first_point().y()); + chain_and_reorder_extrusion_paths(paths, &start_pt); } else { if (overhangs_reverse && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers) { // Always reverse if detect overhang wall is not enabled @@ -216,7 +217,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime ExtrusionPath path(role); //BBS. - path.polyline = polygon.split_at_first_point(); + path.polyline = Polyline3(polygon.split_at_first_point()); path.mm3_per_mm = extrusion_mm3_per_mm; path.width = extrusion_width; path.height = (float)perimeter_generator.layer_height; @@ -429,7 +430,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p Polylines be_clipped; for (const ExtrusionPath &p : it.second) { - be_clipped.emplace_back(std::move(p.polyline)); + be_clipped.emplace_back(p.polyline.to_polyline()); } BoundingBox extrusion_bboxs = get_extents(be_clipped); @@ -463,11 +464,15 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p }; std::unordered_map point_occurrence; for (const ExtrusionPath& path : paths) { - ++point_occurrence[path.polyline.first_point()].occurrence; - ++point_occurrence[path.polyline.last_point()].occurrence; + const Point3 &first_p3 = path.polyline.first_point(); + const Point3 &last_p3 = path.polyline.last_point(); + Point first_p = Point(first_p3.x(), first_p3.y()); + Point last_p = Point(last_p3.x(), last_p3.y()); + ++point_occurrence[first_p].occurrence; + ++point_occurrence[last_p].occurrence; if (path.role() == erOverhangPerimeter) { - point_occurrence[path.polyline.first_point()].is_overhang = true; - point_occurrence[path.polyline.last_point()].is_overhang = true; + point_occurrence[first_p].is_overhang = true; + point_occurrence[last_p].is_overhang = true; } } @@ -655,11 +660,13 @@ bool paths_touch(const ExtrusionPath &path_one, const ExtrusionPath &path_two, d { AABBTreeLines::LinesDistancer lines_two{path_two.as_polyline().lines()}; for (size_t pt_idx = 0; pt_idx < path_one.polyline.size(); pt_idx++) { - if (lines_two.distance_from_lines(path_one.polyline.points[pt_idx]) < limit_distance) { return true; } + const Point3 &p3 = path_one.polyline.points[pt_idx]; + if (lines_two.distance_from_lines(Point(p3.x(), p3.y())) < limit_distance) { return true; } } AABBTreeLines::LinesDistancer lines_one{path_one.as_polyline().lines()}; for (size_t pt_idx = 0; pt_idx < path_two.polyline.size(); pt_idx++) { - if (lines_one.distance_from_lines(path_two.polyline.points[pt_idx]) < limit_distance) { return true; } + const Point3 &p3 = path_two.polyline.points[pt_idx]; + if (lines_one.distance_from_lines(Point(p3.x(), p3.y())) < limit_distance) { return true; } } return false; } @@ -1013,7 +1020,7 @@ std::tuple, Polygons> generate_extra_perimeters_over // polyline) bool first_overhang_is_closed_and_anchored = (overhang_region.front().first_point() == overhang_region.front().last_point() && - !intersection_pl(overhang_region.front().polyline, optimized_lower_slices).empty()); + !intersection_pl(overhang_region.front().polyline.to_polyline(), optimized_lower_slices).empty()); auto is_anchored = [&lower_layer_aabb_tree](const ExtrusionPath &path) { return lower_layer_aabb_tree.distance_from_lines(path.first_point()) <= 0 || @@ -1025,7 +1032,8 @@ std::tuple, Polygons> generate_extra_perimeters_over size_t min_dist_idx = 0; double min_dist = std::numeric_limits::max(); for (size_t i = 0; i < overhang_region.front().polyline.size(); i++) { - Point p = overhang_region.front().polyline[i]; + const Point3 &p3 = overhang_region.front().polyline.points[i]; + Point p = Point(p3.x(), p3.y()); if (double d = lower_layer_aabb_tree.distance_from_lines(p) < min_dist) { min_dist = d; min_dist_idx = i; diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp index 80c4d9815a..eea69b1195 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -1,6 +1,7 @@ #include "Point.hpp" #include "Line.hpp" #include "MultiPoint.hpp" +#include "Polyline.hpp" #include "Int128.hpp" #include "BoundingBox.hpp" #include @@ -257,4 +258,80 @@ int cross(const Vec2crd &v1, const Vec2crd &v2) } +// Point3 utility functions for ZAA (Z Anti-Aliasing) +Polyline to_polyline(const Points &points) { return Polyline(points); } +Polyline3 to_polyline(const Points3 &points) { return Polyline3(points); } + +Points to_points(const Points3 &points3) { + Points points2; + points2.reserve(points3.size()); + for (const Point3 &pt : points3) { + points2.push_back(pt.to_point()); + } + return points2; +} + +// Point3 method implementations +void Point3::rotate(double angle, const Point3 ¢er) { + Vec3crd diff = *this - center; + Point3 temp(diff.x(), diff.y(), diff.z()); + temp.rotate(angle); + Vec3crd sum = temp + center; + *this = Point3(sum.x(), sum.y(), sum.z()); +} + +int Point3::nearest_point_index(const Points &points) const { + return this->to_point().nearest_point_index(points); +} + +bool Point3::nearest_point(const Points &points, Point3* point) const { + Point pt2d; + bool result = this->to_point().nearest_point(points, &pt2d); + if (result && point) { + *point = Point3(pt2d, this->z()); + } + return result; +} + +double Point3::ccw(const Point3 &p1, const Point3 &p2) const { + return this->to_point().ccw(p1.to_point(), p2.to_point()); +} + +double Point3::ccw(const Line3 &line) const { + // Convert to 2D and use existing Point ccw implementation + Point a2d(line.a.x(), line.a.y()); + Point b2d(line.b.x(), line.b.y()); + return this->to_point().ccw(Line(a2d, b2d)); +} + +double Point3::ccw_angle(const Point3 &p1, const Point3 &p2) const { + return this->to_point().ccw_angle(p1.to_point(), p2.to_point()); +} + +Point3 Point3::projection_onto(const MultiPoint3 &poly) const { + // TODO: Implement proper 3D projection when MultiPoint3 conversion methods are ready + // For now, stub implementation + return *this; +} + +Point3 Point3::projection_onto(const Line3 &line) const { + // Project in 2D plane and interpolate Z + Point pt2d = this->to_point(); + Point line_a(line.a.x(), line.a.y()); + Point line_b(line.b.x(), line.b.y()); + Line line2d(line_a, line_b); + Point proj2d = pt2d.projection_onto(line2d); + + // Interpolate Z coordinate + double line_len = line.length(); + if (line_len < EPSILON) { + return Point3(proj2d, line.a.z()); + } + double dist_from_a = (proj2d - line_a).cast().norm(); + double t = dist_from_a / line_len; + t = std::clamp(t, 0.0, 1.0); + coord_t z = coord_t(line.a.z() + t * (line.b.z() - line.a.z())); + return Point3(proj2d, z); +} + } diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index eda75a2b2f..f75bbb9a46 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -21,8 +21,13 @@ namespace Slic3r { class BoundingBox; class BoundingBoxf; class Line; +class Line3; class MultiPoint; +class MultiPoint3; class Point; +class Point3; +class Polyline; +class Polyline3; using Vector = Point; // Base template for eigen derived vectors @@ -57,7 +62,7 @@ using PointsAllocator = tbb::scalable_allocator; using Points = std::vector>; using PointPtrs = std::vector; using PointConstPtrs = std::vector; -using Points3 = std::vector; +using Points3 = std::vector; using Pointfs = std::vector; using Vec2ds = std::vector; using Pointf3s = std::vector; @@ -79,6 +84,11 @@ using Transform2d = Eigen::Transform; using Transform3d = Eigen::Transform; +// Utility functions for Point/Polyline conversion +Polyline to_polyline(const Points &points); +Polyline3 to_polyline(const Points3 &points); +Points to_points(const Points3 &points); + // using ColorRGBA = std::array; // I don't know why Eigen::Transform::Identity() return a const object... template Transform identity() { return Transform::Identity(); } @@ -258,6 +268,115 @@ inline Point operator* (const Point& l, const double& r) return { coord_t(l.x() * r), coord_t(l.y() * r) }; } +// Point3 class - 3D point with Z coordinate for non-planar printing (ZAA) +class Point3 : public Vec3crd { +public: + using coord_type = coord_t; + + Point3() : Vec3crd(0, 0, 0) {} + Point3(int32_t x, int32_t y, int32_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {} + Point3(int64_t x, int64_t y, int64_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {} + Point3(double x, double y, double z = 0.0) : Vec3crd(coord_t(std::round(x)), coord_t(std::round(y)), coord_t(std::round(z))) {} + Point3(const Point3 &rhs) { *this = rhs; } + explicit Point3(const Point &rhs, coord_t z = 0) : Vec3crd(rhs.x(), rhs.y(), z) {} + explicit Point3(const Vec3crd &vec3crd) : Vec3crd(vec3crd) {} + + static Point3 new_scale(coordf_t x, coordf_t y, coordf_t z) { + return Point3(coord_t(scale_(x)), coord_t(scale_(y)), coord_t(scale_(z))); + } + static Point3 new_scale(const Vec3d &v) { + return Point3(coord_t(scale_(v.x())), coord_t(scale_(v.y())), coord_t(scale_(v.z()))); + } + static Point3 new_scale(const Vec3f &v) { + return Point3(coord_t(scale_(v.x())), coord_t(scale_(v.y())), coord_t(scale_(v.z()))); + } + + // Assignment operator for Eigen expressions + template + Point3& operator=(const Eigen::MatrixBase &other) + { + this->Vec3crd::operator=(other); + return *this; + } + + Point3& operator+=(const Point3& rhs) { this->x() += rhs.x(); this->y() += rhs.y(); this->z() += rhs.z(); return *this; } + Point3& operator-=(const Point3& rhs) { this->x() -= rhs.x(); this->y() -= rhs.y(); this->z() -= rhs.z(); return *this; } + Point3& operator*=(const double &rhs) { + this->x() = coord_t(this->x() * rhs); + this->y() = coord_t(this->y() * rhs); + this->z() = coord_t(this->z() * rhs); + return *this; + } + Point3 operator*(const double &rhs) const { return Point3(this->x() * rhs, this->y() * rhs, this->z() * rhs); } + + bool both_comp(const Point3 &rhs, const std::string& op) { + if (op == ">") + return this->x() > rhs.x() && this->y() > rhs.y(); + else if (op == "<") + return this->x() < rhs.x() && this->y() < rhs.y(); + return false; + } + bool any_comp(const Point3 &rhs, const std::string &op) + { + if (op == ">") + return this->x() > rhs.x() || this->y() > rhs.y(); + else if (op == "<") + return this->x() < rhs.x() || this->y() < rhs.y(); + return false; + } + bool any_comp(const coord_t val, const std::string &op) + { + if (op == ">") + return this->x() > val || this->y() > val; + else if (op == "<") + return this->x() < val || this->y() < val; + return false; + } + + void rotate(double angle) { this->rotate(std::cos(angle), std::sin(angle)); } + void rotate(double cos_a, double sin_a) { + double cur_x = (double)this->x(); + double cur_y = (double)this->y(); + this->x() = (coord_t)round(cos_a * cur_x - sin_a * cur_y); + this->y() = (coord_t)round(cos_a * cur_y + sin_a * cur_x); + } + void rotate(double angle, const Point3 ¢er); + + Point3 rotated(double angle) const { Point3 res(*this); res.rotate(angle); return res; } + Point3 rotated(double cos_a, double sin_a) const { Point3 res(*this); res.rotate(cos_a, sin_a); return res; } + Point3 rotated(double angle, const Point3 ¢er) const { Point3 res(*this); res.rotate(angle, center); return res; } + Point3 rotate_90_degree_ccw() const { return Point3(-this->y(), this->x(), this->z()); } + + int nearest_point_index(const Points &points) const; + bool nearest_point(const Points &points, Point3* point) const; + double ccw(const Point3 &p1, const Point3 &p2) const; + double ccw(const Line3 &line) const; + double ccw_angle(const Point3 &p1, const Point3 &p2) const; + Point3 projection_onto(const MultiPoint3 &poly) const; + Point3 projection_onto(const Line3 &line) const; + + // Convert to 2D Point by dropping Z coordinate + Point to_point() const { + return Point(this->x(), this->y()); + } + + double distance_to(const Point3 &point) const { return (point - *this).cast().norm(); } +}; + +// Utility function to convert Points3 to Points +inline void append_points(Points &dst, const Points3 &src) { + std::transform(src.begin(), src.end(), + std::back_inserter(dst), + [](const Point3 &pt) { + return pt.to_point(); + }); +} + +inline Point3 operator* (const Point3& l, const double& r) +{ + return { coord_t(l.x() * r), coord_t(l.y() * r), coord_t(l.z() * r) }; +} + inline std::ostream &operator<<(std::ostream &os, const Point &pt) { os << unscale_(pt.x()) << "," << unscale_(pt.y()); diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index b30564f3c1..8ed180c72d 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -612,6 +612,28 @@ std::pair foot_pt(const Points &polyline, const Point &pt) return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min); } +std::pair foot_pt(const Points3 &polyline, const Point3 &pt) +{ + if (polyline.size() < 2) return std::make_pair(-1, Point3(0, 0, 0)); + + auto d2_min = std::numeric_limits::max(); + Point3 foot_pt_min; + Point3 prev = polyline.front(); + auto it = polyline.begin(); + auto it_proj = polyline.begin(); + for (++it; it != polyline.end(); ++it) { + Point3 foot_pt = pt.projection_onto(Line3(prev, *it)); + double d2 = (foot_pt - pt).cast().squaredNorm(); + if (d2 < d2_min) { + d2_min = d2; + foot_pt_min = foot_pt; + it_proj = it; + } + prev = *it; + } + return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min); +} + ThickLines ThickPolyline::thicklines() const { ThickLines lines; @@ -650,4 +672,217 @@ Lines3 Polyline3::lines() const return lines; } +// Polyline3 ZAA methods implementation +Polyline Polyline3::to_polyline() const { + Polyline out; + out.points.reserve(this->points.size()); + for (const Point3 &point : this->points) { + out.points.emplace_back(point.x(), point.y()); + } + return out; +} + +void Polyline3::clip_end(double distance) { + size_t remove_after_index = this->size(); + while (distance > 0) { + Vec3d last_point = this->last_point().cast(); + this->points.pop_back(); + remove_after_index--; + if (this->points.empty()) { + this->fitting_result.clear(); + return; + } + Vec3d v = this->last_point().cast() - last_point; + double lsqr = v.squaredNorm(); + if (lsqr > distance * distance) { + Vec3d result = last_point + v * (distance / sqrt(lsqr)); + this->points.emplace_back(Point3(coord_t(result.x()), coord_t(result.y()), coord_t(result.z()))); + break; + } + distance -= sqrt(lsqr); + } + + // Clear fitting result if it's affected + if (!fitting_result.empty()) { + while (!fitting_result.empty() && fitting_result.back().start_point_index >= remove_after_index) + fitting_result.pop_back(); + if (!fitting_result.empty()) { + fitting_result.back().end_point_index = this->points.size() - 1; + } + } +} + +void Polyline3::simplify(double tolerance) { + this->points = MultiPoint3::_douglas_peucker(this->points, tolerance); + this->fitting_result.clear(); +} + +void Polyline3::simplify_by_fitting_arc(double tolerance) { + // For now, just use regular simplify + // Full ZAA implementation would use ArcFitter::do_arc_fitting_and_simplify + this->simplify(tolerance); +} + +bool Polyline3::split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) const +{ + if (index > this->size() - 1) + return false; + + if (index == 0) { + p1->clear(); + p1->append(this->first_point()); + *p2 = *this; + } else if (index == this->size() - 1) { + p2->clear(); + p2->append(this->last_point()); + *p1 = *this; + } else { + // Split first part + p1->clear(); + p1->points.reserve(index + 1); + p1->points.insert(p1->begin(), this->begin(), this->begin() + index + 1); + Point3 new_endpoint; + if (this->split_fitting_result_before_index(index, new_endpoint, p1->fitting_result)) + p1->points.back() = new_endpoint; + + // Split second part + p2->clear(); + p2->points.reserve(this->size() - index); + p2->points.insert(p2->begin(), this->begin() + index, this->end()); + Point3 new_startpoint; + if (this->split_fitting_result_after_index(index, new_startpoint, p2->fitting_result)) + p2->points.front() = new_startpoint; + } + return true; +} + +void Polyline3::append(const Point3& point) { + // Don't append if same as last point + if (!this->empty() && this->last_point() == point) + return; + + this->points.push_back(point); + // Clear fitting result as structure changed + this->fitting_result.clear(); +} + +void Polyline3::append(const Polyline3 &src) { + if (!src.is_valid()) return; + + if (this->points.empty()) { + this->points = src.points; + this->fitting_result = src.fitting_result; + } else { + // Append points + if (!src.points.empty() && !this->points.empty() && this->last_point() == src.points.front()) { + // Skip first point if it's the same as our last point + this->points.insert(this->points.end(), src.points.begin() + 1, src.points.end()); + } else { + this->points.insert(this->points.end(), src.points.begin(), src.points.end()); + } + // Note: Full arc fitting integration would merge fitting_result here + this->fitting_result.clear(); + } +} + +void Polyline3::append_before(const Point3& point) { + // Don't append if same as first point + if (!this->empty() && this->first_point() == point) + return; + + this->points.insert(this->points.begin(), point); + // Clear fitting result as structure changed + this->fitting_result.clear(); +} + +void Polyline3::split_at(Point &point, Polyline3* p1, Polyline3* p2) const { + if (this->points.empty()) return; + + // Check if the point is on the polyline + int index = this->find_point(point); + if (index != -1) { + // The split point is on the polyline + split_at_index(index, p1, p2); + point = p1->is_valid() ? p1->last_point().to_point() : p2->first_point().to_point(); + return; + } + + // Find the line to split at + size_t line_idx = 0; + Point p = this->first_point().to_point(); + double min = (p - point).cast().norm(); + Lines3 lines = this->lines(); + for (Lines3::const_iterator line = lines.begin(); line != lines.end(); ++line) { + Point p_tmp = point.projection_onto(line->to_line()); + if ((p_tmp - point).cast().norm() < min) { + p = p_tmp; + min = (p - point).cast().norm(); + line_idx = line - lines.begin(); + } + } + + // Judge whether the closest point is one vertex of polyline + index = this->find_point(p); + if (index != -1) { + this->split_at_index(index, p1, p2); + } else { + Polyline3 temp; + this->split_at_index(line_idx, p1, &temp); + p1->append_before(Point3(point, p1->last_point().z())); + this->split_at_index(line_idx + 1, &temp, p2); + p2->append_before(Point3(point, p2->first_point().z())); + } + point = p; +} + +void Polyline3::split_at(Point3 &point, Polyline3* p1, Polyline3* p2) const { + Point p = point.to_point(); + this->split_at(p, p1, p2); + point = Point3(p, point.z()); +} + +bool Polyline3::split_at_length(const double length, Polyline3 *p1, Polyline3 *p2) const { + if (this->points.empty()) return false; + if (length < 0 || length > this->length()) { return false; } + + if (length < SCALED_EPSILON) { + p1->clear(); + p1->append_before(this->first_point()); + *p2 = *this; + } else if (is_approx(length, this->length(), SCALED_EPSILON)) { + p2->clear(); + p2->append_before(this->last_point()); + *p1 = *this; + } else { + // Find the line to split at + size_t line_idx = 0; + double acc_length = 0; + Point p = this->first_point().to_point(); + for (const auto &l : this->lines()) { + p = l.b.to_point(); + + const double current_length = l.length(); + if (acc_length + current_length >= length) { + p = lerp(l.a.to_point(), l.b.to_point(), (length - acc_length) / current_length); + break; + } + acc_length += current_length; + line_idx++; + } + + // Judge whether the closest point is one vertex of polyline + int index = this->find_point(p); + if (index != -1) { + this->split_at_index(index, p1, p2); + } else { + Polyline3 temp; + this->split_at_index(line_idx, p1, &temp); + p1->append_before(Point3(p, p1->last_point().z())); + this->split_at_index(line_idx + 1, &temp, p2); + p2->append_before(Point3(p, p2->first_point().z())); + } + } + return true; +} + } diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index bff65f7a2f..6d409d3983 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -251,6 +251,7 @@ bool remove_degenerate(Polylines &polylines); // Returns index of a segment of a polyline and foot point of pt on polyline. std::pair foot_pt(const Points &polyline, const Point &pt); +std::pair foot_pt(const Points3 &polyline, const Point3 &pt); class ThickPolyline : public Polyline { public: @@ -290,7 +291,68 @@ inline ThickPolylines to_thick_polylines(Polylines&& polylines, const coordf_t w class Polyline3 : public MultiPoint3 { public: + Polyline3() {} + explicit Polyline3(const Points3 &points) { this->points = points; } + explicit Polyline3(const Polyline &poly, coord_t z = 0) { + this->points.reserve(poly.points.size()); + for (const Point &pt : poly.points) { + this->points.emplace_back(pt.x(), pt.y(), z); + } + } + virtual Lines3 lines() const; + + // Convert to 2D Polyline by dropping Z coordinates + Polyline to_polyline() const; + + // Clip the end of the polyline by a distance + void clip_end(double distance); + + // Simplify polyline using Douglas-Peucker algorithm + void simplify(double tolerance); + + // Simplify by arc fitting (for ZAA arc fitting support) + void simplify_by_fitting_arc(double tolerance); + + // Reverse the polyline + using MultiPoint3::reverse; + + // Split polyline at given index + bool split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) const; + + // Split polyline at a given point (2D) + void split_at(Point &point, Polyline3* p1, Polyline3* p2) const; + + // Split polyline at a given point (3D) + void split_at(Point3 &point, Polyline3* p1, Polyline3* p2) const; + + // Split polyline at a given length + bool split_at_length(const double length, Polyline3 *p1, Polyline3 *p2) const; + + // Append a single point + void append(const Point3& point); + + // Append another Polyline3 + void append(const Polyline3& src); + + // Append before (prepend) + void append_before(const Point3& point); + + // Arc fitting support - fitting_result stores arc path data + // This is populated by simplify_by_fitting_arc() + // Uses the global PathFittingData from ArcFitter.hpp + std::vector fitting_result; + +private: + // Helper methods for split_at_index + bool split_fitting_result_before_index(size_t index, Point3 &new_endpoint, std::vector &result) const { + // Simplified stub - full implementation would handle arc fitting data + return false; + } + bool split_fitting_result_after_index(size_t index, Point3 &new_startpoint, std::vector &result) const { + // Simplified stub - full implementation would handle arc fitting data + return false; + } }; typedef std::vector Polylines3; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 4656665a18..e8b393b81f 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -953,6 +953,8 @@ static std::vector s_Preset_print_options { "enable_wrapping_detection", "seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "scarf_joint_speed", "scarf_joint_flow_ratio", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls", "scarf_overhang_threshold", "interlocking_beam", "interlocking_orientation", "interlocking_beam_layer_count", "interlocking_depth", "interlocking_boundary_avoidance", "interlocking_beam_width","calib_flowrate_topinfill_special_order", + // Z Anti-Aliasing (ZAA) + "zaa_enabled", "zaa_minimize_perimeter_height", "zaa_dont_alternate_fill_direction", "zaa_min_z", "zaa_region_disable", "ironing_expansion", }; static std::vector s_Preset_filament_options {/*"filament_colour", */ "default_filament_colour", "required_nozzle_HRC", "filament_diameter", "pellet_flow_coefficient", "volumetric_speed_coefficients", "filament_type", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index b7ab12ee0d..88119e4983 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2148,6 +2148,17 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) } } + // Z-Contouring + for (PrintObject *obj : m_objects) { + bool need_contouring = need_slicing_objects.count(obj) != 0 && obj->config().zaa_enabled; + if (need_contouring) { + obj->contour_z(); + } else { + if (obj->set_started(posContouring)) + obj->set_done(posContouring); + } + } + tbb::parallel_for(tbb::blocked_range(0, int(m_objects.size())), [this, need_slicing_objects](const tbb::blocked_range& range) { for (int i = range.begin(); i < range.end(); i++) { @@ -2186,6 +2197,8 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) obj->set_done(posInfill); if (obj->set_started(posIroning)) obj->set_done(posIroning); + if (obj->set_started(posContouring)) + obj->set_done(posContouring); if (obj->set_started(posSupportMaterial)) obj->set_done(posSupportMaterial); if (obj->set_started(posDetectOverhangsForLift)) @@ -2565,7 +2578,7 @@ void Print::_make_skirt() flow.width(), (float)initial_layer_print_height // this will be overridden at G-code export time ))); - eloop.paths.back().polyline = loop.split_at_first_point(); + eloop.paths.back().polyline = Polyline3(loop.split_at_first_point()); m_skirt.append(eloop); if (m_config.min_skirt_length.value > 0) { // The skirt length is limited. Sum the total amount of filament length extruded, in mm. @@ -2623,7 +2636,7 @@ void Print::_make_skirt() flow.width(), (float)initial_layer_print_height // this will be overridden at G-code export time ))); - eloop.paths.back().polyline = loop.split_at_first_point(); + eloop.paths.back().polyline = Polyline3(loop.split_at_first_point()); object->m_skirt.append(std::move(eloop)); if (m_config.min_skirt_length.value > 0) { // The skirt length is limited. Sum the total amount of filament length extruded, in mm. @@ -4018,7 +4031,8 @@ static void from_json(const json& j, Polyline& poly_line) { } static void from_json(const json& j, ExtrusionPath& extrusion_path) { - extrusion_path.polyline = j[JSON_EXTRUSION_POLYLINE]; + Polyline temp_polyline = j[JSON_EXTRUSION_POLYLINE]; + extrusion_path.polyline = Polyline3(temp_polyline); extrusion_path.mm3_per_mm = j[JSON_EXTRUSION_MM3_PER_MM]; extrusion_path.width = j[JSON_EXTRUSION_WIDTH]; extrusion_path.height = j[JSON_EXTRUSION_HEIGHT]; @@ -4852,8 +4866,9 @@ ExtrusionLayers FakeWipeTower::getTrueExtrusionLayersFromWipeTower() const paths.reserve(it->second.size()); for (auto &polyline : it->second) { ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, layer_heights[index]); - path.polyline = polyline; - for (auto &p : path.polyline.points) p += trans; + path.polyline = Polyline3(polyline); + Point3 trans3(trans, 0); + for (auto &p : path.polyline.points) p += trans3; paths.push_back(path); } el.paths = std::move(paths); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index dafca39387..0871ac3c1d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -92,7 +92,7 @@ enum PrintStep { enum PrintObjectStep { posSlice, posPerimeters,posEstimateCurledExtrusions, posPrepareInfill, - posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath, + posInfill, posIroning, posContouring, posSupportMaterial, posSimplifyPath, posSimplifySupportPath, // BBS posDetectOverhangsForLift, posSimplifyWall, posSimplifyInfill, @@ -496,6 +496,7 @@ private: void prepare_infill(); void infill(); void ironing(); + void contour_z(); void generate_support_material(); void estimate_curled_extrusions(); void simplify_extrusion_path(); @@ -642,14 +643,14 @@ struct FakeWipeTower std::vector paths; for (float h = 0.f; h < height; h += layer_height) { ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, layer_height); - path.polyline = {minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner}; + path.polyline = Polyline3(Polyline{{minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner}}); paths.push_back({path}); if (h == 0.f) { // add brim ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, layer_height); Point wtbminCorner = {minCorner - Point{bd, bd}}; Point wtbmaxCorner = {maxCorner + Point{bd, bd}}; - fakeBrim.polyline = {wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner}; + fakeBrim.polyline = Polyline3(Polyline{{wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner}}); paths.back().push_back(fakeBrim); } } @@ -686,13 +687,13 @@ struct FakeWipeTower ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, lh); - path.polyline = { minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }; + path.polyline = Polyline3(Polyline{{ minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }}); paths.push_back({ path }); // We added the border, now add several parallel lines so we can detect an object that is fully inside the tower. // For now, simply use fixed spacing of 3mm. for (coord_t y=minCorner.y()+scale_(3.); ymode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("ironing_expansion", coFloat); + def->label = L("Ironing expansion"); + def->category = L("Quality"); + def->tooltip = L("Expand or contract the ironing area."); + def->sidetext = L("mm"); + def->min = -100; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0)); + + def = this->add("zaa_region_disable", coBool); + def->label = L("Disable Z contouring for region"); + def->category = L("Quality"); + def->tooltip = L("Disable Z contouring for this specific region"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("zaa_enabled", coBool); + def->label = L("Z contouring enabled"); + def->category = L("Quality"); + def->tooltip = L("Enable Z-layer contouring (aka Z-layer anti-aliasing)"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("zaa_minimize_perimeter_height", coFloat); + def->label = L("Minimize wall height angle"); + def->category = L("Quality"); + def->tooltip = L("Reduce top surface perimeter heights to match height of edge for perimeters less than this angle. Set 0 to disable."); + def->sidetext = L("°"); + def->min = 0; + def->max = 90; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(35)); + + def = this->add("zaa_dont_alternate_fill_direction", coBool); + def->label = L("Don't alternate fill direction"); + def->category = L("Quality"); + def->tooltip = L("Disable alternating fill direction when using Z contouring"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("zaa_min_z", coFloat); + def->label = L("Minimum z height"); + def->category = L("Quality"); + def->tooltip = L("Minimum z layer height. Also controls slicing plane"); + def->sidetext = L("mm"); + def->min = 0; + def->max = 100; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionFloat(0.05)); + def = this->add("layer_change_gcode", coString); def->label = L("Layer change G-code"); def->tooltip = L("This G-code is inserted at every layer change after the Z lift."); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index edc759d7d6..26cabab92b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1027,6 +1027,11 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionInt, interlocking_depth)) ((ConfigOptionInt, interlocking_boundary_avoidance)) + // Z Anti-Aliasing (aka Z Contouring) + ((ConfigOptionBool, zaa_enabled)) + ((ConfigOptionBool, zaa_dont_alternate_fill_direction)) + ((ConfigOptionFloat, zaa_min_z)) + // Orca: internal use only ((ConfigOptionBool, calib_flowrate_topinfill_special_order)) // ORCA: special flag for flow rate calibration ) @@ -1101,6 +1106,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, ironing_speed)) ((ConfigOptionFloat, ironing_angle)) ((ConfigOptionBool, ironing_angle_fixed)) + ((ConfigOptionFloat, ironing_expansion)) // Filament Ironing ((ConfigOptionPercentsNullable, filament_ironing_flow)) ((ConfigOptionFloatsNullable, filament_ironing_spacing)) @@ -1189,6 +1195,10 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, scarf_joint_speed)) ((ConfigOptionFloat, scarf_joint_flow_ratio)) ((ConfigOptionPercent, scarf_overhang_threshold)) + + // Z Anti-Aliasing (aka Z Contouring) + ((ConfigOptionBool, zaa_region_disable)) + ((ConfigOptionFloat, zaa_minimize_perimeter_height)) ) PRINT_CONFIG_CLASS_DEFINE( diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index fc68fce76b..d9f41a69a8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -8,6 +8,7 @@ #include "Layer.hpp" #include "MutablePolygon.hpp" #include "PrintConfig.hpp" +#include "SLA/IndexedMesh.hpp" #include "Support/SupportMaterial.hpp" #include "Support/SupportSpotsGenerator.hpp" #include "Support/TreeSupport.hpp" @@ -23,6 +24,10 @@ #include "AABBTreeLines.hpp" #include +#include +#include +#include +#include #include #include #include @@ -709,6 +714,46 @@ void PrintObject::ironing() } } +void PrintObject::contour_z() +{ + if (!this->set_started(posContouring)) { + return; + } + + m_print->set_status(40, L("Z contouring")); + BOOST_LOG_TRIVIAL(debug) << "Contouring in parallel - start"; + + TriangleMesh mesh = this->m_model_object->raw_mesh(); + if (m_model_object->instances.size() != 1) { + throw RuntimeError("ContourZ: unexpected number of instances"); + } + + m_model_object->instances.front()->transform_mesh(&mesh, true); + sla::IndexedMesh imesh(mesh); + + std::mutex mtx; + size_t completed = 0; + tbb::parallel_for( + // Contouring starting with layer second layer to avoid build plate collision + tbb::blocked_range(1, m_layers.size()), + [&, this](const tbb::blocked_range& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); layer_idx++) { + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_contour_z(imesh); + + std::scoped_lock lock(mtx); + completed++; + std::string msg = (boost::format("Z contoured layer %d/%d (%d%%)") % (completed) % m_layers.size() % int(double(completed) / m_layers.size() * 100)).str(); + m_print->set_status(40, msg); + } + } + ); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Contouring in parallel - end"; + + this->set_done(posContouring); +} + // BBS void PrintObject::clear_overhangs_for_lift() { @@ -1351,15 +1396,15 @@ bool PrintObject::invalidate_step(PrintObjectStep step) // propagate to dependent steps if (step == posPerimeters) { - invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posSimplifyPath, posSimplifyInfill }); + invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posContouring, posSimplifyPath, posSimplifyInfill }); invalidated |= m_print->invalidate_steps({ psSkirtBrim }); } else if (step == posPrepareInfill) { - invalidated |= this->invalidate_steps({ posInfill, posIroning, posSimplifyPath, posSimplifyInfill }); + invalidated |= this->invalidate_steps({ posInfill, posIroning, posContouring, posSimplifyPath, posSimplifyInfill }); } else if (step == posInfill) { - invalidated |= this->invalidate_steps({ posIroning, posSimplifyInfill }); + invalidated |= this->invalidate_steps({ posIroning, posContouring, posSimplifyInfill }); invalidated |= m_print->invalidate_steps({ psSkirtBrim }); } else if (step == posSlice) { - invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifyInfill }); + invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posContouring, posSupportMaterial, posSimplifyPath, posSimplifyInfill }); invalidated |= m_print->invalidate_steps({ psSkirtBrim }); m_slicing_params.valid = false; } else if (step == posSupportMaterial) { diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 532307414d..5d51cf3cf7 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -4,6 +4,7 @@ #include "ClipperUtils.hpp" #include "ElephantFootCompensation.hpp" +#include "Exception.hpp" #include "I18N.hpp" #include "Layer.hpp" #include "MultiMaterialSegmentation.hpp" @@ -34,6 +35,13 @@ LayerPtrs new_layers( coordf_t lo = object_layers[i_layer]; coordf_t hi = object_layers[i_layer + 1]; coordf_t slice_z = 0.5 * (lo + hi); + if (print_object->config().zaa_enabled) { + coordf_t z_offset = print_object->config().zaa_min_z; + slice_z = lo + z_offset; + if (slice_z < lo || slice_z > hi) { + throw RuntimeError("Bad min Z value"); + } + } Layer *layer = new Layer(id ++, print_object, hi - lo, hi + zmin, slice_z); out.emplace_back(layer); if (prev != nullptr) { diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index 2b017b709b..c1325a4264 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -1000,7 +1000,7 @@ std::vector> chain_segments_greedy2(SegmentEndPointFunc std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near) { - auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; + auto segment_end_point = [&entities](size_t idx, bool first_point) -> Point { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; std::vector> out = chain_segments_greedy_constrained_reversals(segment_end_point, could_reverse, entities.size(), start_near); for (std::pair &segment : out) { @@ -1028,6 +1028,11 @@ void reorder_extrusion_entities(std::vector &entities, const s entities.swap(out); } +void chain_and_reorder_extrusion_entities(std::vector &entities, const Point &start_near) +{ + chain_and_reorder_extrusion_entities(entities, &start_near); +} + void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near) { // this function crashes if there are empty elements in entities @@ -1038,7 +1043,7 @@ void chain_and_reorder_extrusion_entities(std::vector &entitie std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) { - auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); }; + auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> Point { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); }; return chain_segments_greedy(segment_end_point, extrusion_paths.size(), start_near); } diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index f8956afaa3..88389b20de 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -20,6 +20,7 @@ std::vector chain_expolygons(const ExPolygons &input_exploy); std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain); +void chain_and_reorder_extrusion_entities(std::vector &entities, const Point &start_near); void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 9159de617f..ff7cfdb5c8 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -1163,7 +1163,7 @@ static void modulate_extrusion_by_overlapping_layers( for (ExtrusionEntity *ee : extrusions_in_out) { ExtrusionPath *path = dynamic_cast(ee); assert(path != nullptr); - polylines.emplace_back(Polyline(std::move(path->polyline))); + polylines.emplace_back(path->polyline.to_polyline()); path_ends.emplace_back(std::pair(polylines.back().points.front(), polylines.back().points.back())); delete path; } @@ -1288,9 +1288,10 @@ static void modulate_extrusion_by_overlapping_layers( if (! path->polyline.points.empty()) path->polyline.points.pop_back(); // Consume the fragment's polyline, remove it from the input fragments, so it will be ignored the next time. - path->polyline.append(std::move(frag_polyline)); + path->polyline.append(Polyline3(std::move(frag_polyline))); frag_polyline.points.clear(); - pt_current = path->polyline.points.back(); + const Point3 &pt_back3 = path->polyline.points.back(); + pt_current = Point(pt_back3.x(), pt_back3.y()); if (pt_current == pt_end) { // End of the path. break; diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index d1fa5d8627..2392908a0b 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1181,7 +1181,9 @@ namespace SupportMaterialInternal { // This is a complete loop. // Add the outer contour first. Polygon poly; - poly.points = ep.polyline.points; + // Convert Points3 to Points + for (const Point3 &p3 : ep.polyline.points) + poly.points.emplace_back(p3.x(), p3.y()); poly.points.pop_back(); if (poly.area() < 0) poly.reverse(); diff --git a/src/libslic3r/VariableWidth.cpp b/src/libslic3r/VariableWidth.cpp index e01050e4a4..c00fc13bdf 100644 --- a/src/libslic3r/VariableWidth.cpp +++ b/src/libslic3r/VariableWidth.cpp @@ -16,11 +16,11 @@ ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline& thick_polyl if (line_len < SCALED_EPSILON) { // The line is so tiny that we don't care about its width when we connect it to another line. if (!path.empty()) - path.polyline.points.back() = line.b; // If the variable path is non-empty, connect this tiny line to it. + path.polyline.points.back() = Point3(line.b); // If the variable path is non-empty, connect this tiny line to it. else if (i + 1 < (int)lines.size()) // If there is at least one following line, connect this tiny line to it. lines[i + 1].a = line.a; else if (!multi_path.paths.empty()) - multi_path.paths.back().polyline.points.back() = line.b; // Connect this tiny line to the last finished path. + multi_path.paths.back().polyline.points.back() = Point3(line.b); // Connect this tiny line to the last finished path. // If any of the above isn't satisfied, then remove this tiny line. continue; @@ -65,8 +65,8 @@ ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline& thick_polyl const double w = fmax(line.a_width, line.b_width); const Flow new_flow = (role == erOverhangPerimeter && flow.bridge()) ? flow : flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); if (path.polyline.points.empty()) { - path.polyline.append(line.a); - path.polyline.append(line.b); + path.polyline.append(Point3(line.a)); + path.polyline.append(Point3(line.b)); // Convert from spacing to extrusion width based on the extrusion model // of a square extrusion ended with semi circles. #ifdef SLIC3R_DEBUG @@ -81,7 +81,7 @@ ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline& thick_polyl if (thickness_delta <= merge_tolerance) { // the width difference between this line and the current flow // (of the previous line) width is within the accepted tolerance - path.polyline.append(line.b); + path.polyline.append(Point3(line.b)); } else { // we need to initialize a new line multi_path.paths.emplace_back(std::move(path)); @@ -124,13 +124,13 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths_2(const ThickPolyline& t path = ExtrusionPath(role); double length = lines[start_index].length(); double sum = lines[start_index].length() * 0.5 * (lines[start_index].a_width + lines[start_index].b_width); - path.polyline.append(lines[start_index].a); + path.polyline.append(Point3(lines[start_index].a)); for (int idx = start_index + 1; idx < i; idx++) { length += lines[idx].length(); sum += lines[idx].length() * 0.5 * (lines[idx].a_width + lines[idx].b_width); - path.polyline.append(lines[idx].a); + path.polyline.append(Point3(lines[idx].a)); } - path.polyline.append(lines[i].a); + path.polyline.append(Point3(lines[i].a)); if (length > SCALED_EPSILON) { double w = sum / length; Flow new_flow = flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); @@ -193,13 +193,13 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths_2(const ThickPolyline& t path = ExtrusionPath(role); double length = lines[start_index].length(); double sum = lines[start_index].length() * lines[start_index].a_width; - path.polyline.append(lines[start_index].a); + path.polyline.append(Point3(lines[start_index].a)); for (int idx = start_index + 1; idx < final_size; idx++) { length += lines[idx].length(); sum += lines[idx].length() * lines[idx].a_width; - path.polyline.append(lines[idx].a); + path.polyline.append(Point3(lines[idx].a)); } - path.polyline.append(lines[final_size - 1].b); + path.polyline.append(Point3(lines[final_size - 1].b)); if (length > SCALED_EPSILON) { double w = sum / length; Flow new_flow = flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); diff --git a/src/libslic3r/libslic3r_version.h.in b/src/libslic3r/libslic3r_version.h.in index 750e092d28..df2a92f75b 100644 --- a/src/libslic3r/libslic3r_version.h.in +++ b/src/libslic3r/libslic3r_version.h.in @@ -5,6 +5,7 @@ #define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@" #define SLIC3R_VERSION "@SLIC3R_VERSION@" #define SoftFever_VERSION "@SoftFever_VERSION@" +#define ZAA_VERSION "@ZAA_VERSION@" #ifndef GIT_COMMIT_HASH #define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized #endif diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6cc33dc169..716056049d 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1991,7 +1991,7 @@ void _3DScene::thick_lines_to_verts( // Fill in the qverts and tverts with quads and triangles for the extrusion_path. void _3DScene::extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry) { - Polyline polyline = extrusion_path.polyline; + Polyline polyline = extrusion_path.polyline.to_polyline(); polyline.remove_duplicate_points(); polyline.translate(copy); const Lines lines = polyline.lines(); @@ -2007,7 +2007,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, flo std::vector widths; std::vector heights; for (const ExtrusionPath& extrusion_path : extrusion_loop.paths) { - Polyline polyline = extrusion_path.polyline; + Polyline polyline = extrusion_path.polyline.to_polyline(); polyline.remove_duplicate_points(); polyline.translate(copy); const Lines lines_this = polyline.lines(); @@ -2025,7 +2025,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_mult std::vector widths; std::vector heights; for (const ExtrusionPath& extrusion_path : extrusion_multi_path.paths) { - Polyline polyline = extrusion_path.polyline; + Polyline polyline = extrusion_path.polyline.to_polyline(); polyline.remove_duplicate_points(); polyline.translate(copy); const Lines lines_this = polyline.lines(); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6c8dd5f421..cbf930155a 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2567,13 +2567,32 @@ void GUI_App::init_single_instance_checker(const std::string &name, const std::s m_single_instance_checker = std::make_unique(boost::nowide::widen(name), boost::nowide::widen(path)); } +bool GUI_App::CallOnInit() +{ + // Override wxApp::CallOnInit to catch exceptions from ~wxMacAutoreleasePool + try { + return wxApp::CallOnInit(); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(fatal) << "Exception in CallOnInit: " << e.what(); + return false; + } catch (...) { + // The app was initialized, just the autorelease pool cleanup threw. + // Return true to let the app continue. + return m_initialized; + } +} + bool GUI_App::OnInit() { try { return on_init_inner(); } catch (const std::exception& e) { BOOST_LOG_TRIVIAL(fatal) << "OnInit Got Fatal error: " << e.what(); - generic_exception_handle(); + flush_logs(); + return false; + } catch (...) { + BOOST_LOG_TRIVIAL(fatal) << "OnInit caught non-std exception"; + flush_logs(); return false; } } @@ -3384,8 +3403,16 @@ bool GUI_App::on_init_network(bool try_backup) Slic3r::NetworkAgentFactory::register_all_agents(); // m_agent = new Slic3r::NetworkAgent(data_directory); - std::unique_ptr agent_ptr = Slic3r::create_agent_from_config(data_directory, app_config); - m_agent = agent_ptr.release(); + try { + std::unique_ptr agent_ptr = Slic3r::create_agent_from_config(data_directory, app_config); + m_agent = agent_ptr.release(); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "Failed to create network agent: " << e.what(); + m_agent = nullptr; + } catch (...) { + BOOST_LOG_TRIVIAL(error) << "Failed to create network agent: unknown exception (code signing?)"; + m_agent = nullptr; + } if (!m_device_manager) m_device_manager = new Slic3r::DeviceManager(m_agent); @@ -5683,6 +5710,7 @@ std::string GUI_App::format_display_version() if (!version_display.empty()) return version_display; version_display = SoftFever_VERSION; + version_display += " / ZAA v" + std::string(ZAA_VERSION); return version_display; } @@ -6514,7 +6542,8 @@ void GUI_App::update_mode() mainframe->m_param_dialog->panel()->update_mode(); if (mainframe->m_printer_view) mainframe->m_printer_view->update_mode(); - mainframe->m_webview->update_mode(); + if (mainframe->m_webview) + mainframe->m_webview->update_mode(); #ifdef _MSW_DARK_MODE if (!wxGetApp().tabs_as_menu()) @@ -6526,8 +6555,6 @@ void GUI_App::update_mode() for (auto tab : model_tabs_list) tab->update_mode(); - //BBS plater()->update_menus(); - plater()->canvas3D()->update_gizmos_on_off_state(); } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index c879b1e5bb..4ca02e5db7 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -324,6 +324,7 @@ public: void on_start_subscribe_again(std::string dev_id); std::string get_local_models_path(); bool OnInit() override; + bool CallOnInit() override; int OnExit() override; bool initialized() const { return m_initialized; } inline bool is_enable_multi_machine() { return this->app_config&& this->app_config->get("enable_multi_machine") == "true"; } diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index acc53d35b8..5431836158 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -102,7 +102,9 @@ std::map> SettingsFactory::OBJECT_C std::map> SettingsFactory::PART_CATEGORY_SETTINGS= { - { L("Quality"), {{"ironing_type", "",8},{"ironing_flow", "",9},{"ironing_spacing", "",10},{"ironing_inset", "", 11},{"bridge_flow", "",11},{"make_overhang_printable", "",11},{"bridge_density", "", 1} + { L("Quality"), {{"ironing_type", "",8},{"ironing_flow", "",9},{"ironing_spacing", "",10},{"ironing_inset", "", 11},{"bridge_flow", "",11},{"make_overhang_printable", "",11},{"bridge_density", "", 1}, + {"ironing_expansion", "", 14}, + {"zaa_enabled", "", 1}, {"zaa_region_disable", "", 2}, {"zaa_minimize_perimeter_height", "", 3}, {"zaa_dont_alternate_fill_direction", "", 4}, {"zaa_min_z", "", 5} }}, { L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},{"top_surface_density", L("Top Surface Density"),1}, {"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},{"bottom_surface_density", L("Bottom Surface Density"),1}, diff --git a/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp b/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp index a966a391f3..8c0e86f4ad 100644 --- a/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp +++ b/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp @@ -303,7 +303,7 @@ static void convert_lines_to_vertices(const Slic3r::Lines& lines, const std::vec static void convert_to_vertices(const Slic3r::ExtrusionPath& extrusion_path, float print_z, size_t layer_id, size_t extruder_id, size_t color_id, EGCodeExtrusionRole extrusion_role, const Slic3r::Point& shift, std::vector& vertices) { - Slic3r::Polyline polyline = extrusion_path.polyline; + Slic3r::Polyline polyline = extrusion_path.polyline.to_polyline(); polyline.remove_duplicate_points(); polyline.translate(shift); const Slic3r::Lines lines = polyline.lines(); @@ -319,7 +319,7 @@ static void convert_to_vertices(const Slic3r::ExtrusionMultiPath& extrusion_mult std::vector widths; std::vector heights; for (const Slic3r::ExtrusionPath& extrusion_path : extrusion_multi_path.paths) { - Slic3r::Polyline polyline = extrusion_path.polyline; + Slic3r::Polyline polyline = extrusion_path.polyline.to_polyline(); polyline.remove_duplicate_points(); polyline.translate(shift); const Slic3r::Lines lines_this = polyline.lines(); @@ -337,7 +337,7 @@ static void convert_to_vertices(const Slic3r::ExtrusionLoop& extrusion_loop, flo std::vector widths; std::vector heights; for (const Slic3r::ExtrusionPath& extrusion_path : extrusion_loop.paths) { - Slic3r::Polyline polyline = extrusion_path.polyline; + Slic3r::Polyline polyline = extrusion_path.polyline.to_polyline(); polyline.remove_duplicate_points(); polyline.translate(shift); const Slic3r::Lines lines_this = polyline.lines(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 8e74eda926..f0fdd9af94 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1,5 +1,7 @@ #include "MainFrame.hpp" +#include +#include #include #include #include @@ -23,6 +25,7 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/PresetBundle.hpp" +#include "libslic3r/Utils.hpp" #include "Tab.hpp" #include "ProgressStatusBar.hpp" @@ -2451,6 +2454,17 @@ void MainFrame::init_menubar_as_editor() open_recent_project(file_id, filename); }, wxID_FILE1, wxID_FILE1 + 49); // [5050, 5100) + std::vector non_planar_projects; + for (auto &&entry : fs::directory_iterator(resources_dir() + "/nonplanar")) { + if (fs::is_regular_file(entry) && entry.path().extension() == ".3mf") { + non_planar_projects.push_back(entry.path().string()); + } + } + std::sort(non_planar_projects.begin(), non_planar_projects.end()); + for (auto &&path : non_planar_projects) { + m_recent_projects.AddFileToHistory(from_u8(path)); + } + std::vector recent_projects = wxGetApp().app_config->get_recent_projects(); std::reverse(recent_projects.begin(), recent_projects.end()); for (const std::string& project : recent_projects) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 5b4f9d3504..eecbe61452 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -621,7 +621,7 @@ void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost:: Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/) { if (!m_config->has(opt_key)) { - std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.\n"; + // Option not in config — may be newly added (e.g. ZAA options) } std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7aa9a24cd3..f12eb94983 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3816,7 +3816,7 @@ static std::vector get_search_inputs(ConfigOptionMode mode) auto& tabs_list = wxGetApp().tabs_list; auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology(); for (auto tab : tabs_list) - if (tab->supports_printer_technology(print_tech)) + if (tab && tab->supports_printer_technology(print_tech) && tab->get_config()) ret.emplace_back(Search::InputInfo {tab->get_config(), tab->type(), mode}); return ret; @@ -3840,7 +3840,9 @@ void Sidebar::update_mode() //obj_list()->get_sizer()->Show(m_mode > comSimple); obj_list()->unselect_objects(); - obj_list()->update_selections(); + // Guard: during startup the 3D canvas selection may not be fully initialized + if (wxGetApp().initialized()) + obj_list()->update_selections(); // obj_list()->update_object_menu(); Layout(); @@ -10384,12 +10386,13 @@ void Plater::priv::set_project_name(const wxString& project_name) { BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << __LINE__ << " project is:" << project_name; m_project_name = project_name; + wxString name = project_name + " - OrcaSlicer-ZAA"; //update topbar title #ifdef __WINDOWS__ - wxGetApp().mainframe->SetTitle(m_project_name + " - OrcaSlicer"); - wxGetApp().mainframe->topbar()->SetTitle(m_project_name); + wxGetApp().mainframe->SetTitle(name); + wxGetApp().mainframe->topbar()->SetTitle(name); #else - wxGetApp().mainframe->SetTitle(m_project_name); + wxGetApp().mainframe->SetTitle(name); if (!m_project_name.IsEmpty()) wxGetApp().mainframe->update_title_colour_after_set_title(); #endif diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 1aef329297..db885c4310 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -70,6 +70,7 @@ static std::string get_key(const std::string &opt_key, Preset::Type type) { retu void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type type, ConfigOptionMode mode) { + if (!config) return; auto emplace = [this, type](const std::string key, const wxString &label) { const GroupAndCategory &gc = groups_and_categories[key]; if (gc.group.IsEmpty() || gc.category.IsEmpty()) return; @@ -89,13 +90,19 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty }; for (std::string opt_key : config->keys()) { - const ConfigOptionDef &opt = config->def()->options.at(opt_key); + auto def_it = config->def()->options.find(opt_key); + if (def_it == config->def()->options.end()) { + continue; + } + const ConfigOptionDef &opt = def_it->second; if (opt.mode > mode) continue; int cnt = 0; - if ((type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER) && opt_key != "printable_area") - switch (config->option(opt_key)->type()) { + if ((type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER || type == Preset::TYPE_PRINT) && opt_key != "printable_area") { + const ConfigOption *opt_ptr = config->option(opt_key); + if (!opt_ptr) continue; + switch (opt_ptr->type()) { case coInts: change_opt_key(opt_key, config, cnt); break; case coBools: change_opt_key(opt_key, config, cnt); break; case coFloats: change_opt_key(opt_key, config, cnt); break; @@ -106,6 +113,7 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty case coEnums: change_opt_key(opt_key, config, cnt); break; default: break; } + } wxString label = opt.full_label.empty() ? opt.label : opt.full_label; @@ -222,7 +230,7 @@ bool OptionsSearcher::search(const std::string &search, bool force /* = false*/, if (full_list) { std::string label = into_u8(get_label(opt)); //all - if (type == Preset::TYPE_INVALID) { + if (type == Preset::TYPE_INVALID) { found.emplace_back(FoundOption{label, label, into_u8(get_tooltip(opt)), i, 0}); } else if (type == opt.type){ found.emplace_back(FoundOption{label, label, into_u8(get_tooltip(opt)), i, 0}); @@ -289,7 +297,9 @@ OptionsSearcher::~OptionsSearcher() {} void OptionsSearcher::init(std::vector input_values) { options.clear(); - for (auto i : input_values) append_options(i.config, i.type, i.mode); + for (size_t idx = 0; idx < input_values.size(); ++idx) { + append_options(input_values[idx].config, input_values[idx].type, input_values[idx].mode); + } sort_options(); search(search_line, true, search_type); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 566aaa93cd..fbb134fd09 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2322,6 +2322,14 @@ void TabPrint::build() optgroup->append_single_option_line("ironing_angle", "quality_settings_ironing#angle-offset"); optgroup->append_single_option_line("ironing_angle_fixed", "quality_settings_ironing#fixed-angle"); + optgroup = page->new_optgroup("Z Contouring", L"param_advanced"); + optgroup->append_single_option_line("zaa_enabled"); + optgroup->append_single_option_line("zaa_region_disable"); + optgroup->append_single_option_line("zaa_minimize_perimeter_height"); + optgroup->append_single_option_line("zaa_dont_alternate_fill_direction"); + optgroup->append_single_option_line("zaa_min_z"); + optgroup->append_single_option_line("ironing_expansion"); + optgroup = page->new_optgroup(L("Wall generator"), L"param_wall_generator"); optgroup->append_single_option_line("wall_generator", "quality_settings_wall_generator"); optgroup->append_single_option_line("wall_transition_angle", "quality_settings_wall_generator#arachne"); diff --git a/version.inc b/version.inc index d6c6b293b3..86d3759e73 100644 --- a/version.inc +++ b/version.inc @@ -8,6 +8,7 @@ if(NOT DEFINED BBL_INTERNAL_TESTING) set(BBL_INTERNAL_TESTING "0") endif() set(SoftFever_VERSION "2.3.2-dev") +set(ZAA_VERSION "1.0.3") string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" SoftFever_VERSION_MATCH ${SoftFever_VERSION}) set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1}) From 710c2a3854d26e5d6dd34ad5e9bebbc108c1b6b2 Mon Sep 17 00:00:00 2001 From: Matthias Nott Date: Mon, 9 Feb 2026 21:16:27 +0100 Subject: [PATCH 02/28] docs: Add build script and step-by-step macOS build guide - build.sh: Handles full lifecycle (deps, configure, incremental build) with auto-detection of architecture, CMake 4.x compat, and preflight checks - BUILD.md: Beginner-friendly walkthrough from zero (no Xcode, no Homebrew, no git) to running OrcaSlicer.app --- BUILD.md | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ build.sh | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 422 insertions(+) create mode 100644 BUILD.md create mode 100755 build.sh diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000000..fe7cb24847 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,215 @@ +# Building OrcaSlicer with ZAA on macOS + +A step-by-step guide to building OrcaSlicer from source on macOS. No prior experience with building software required. + +## What You'll Need + +- A Mac running macOS 11.3 (Big Sur) or later +- About 15 GB of free disk space +- An internet connection +- About 1-2 hours for the first build (mostly waiting) + +## Step 1: Open Terminal + +Press **Cmd + Space**, type **Terminal**, and press Enter. A window with a command prompt will appear. All commands below are typed into this window. + +## Step 2: Install Xcode Command Line Tools + +This installs the compiler and basic development tools. + +```bash +xcode-select --install +``` + +A dialog will pop up. Click **Install** and wait for it to finish (a few minutes). + +To verify it worked: + +```bash +xcode-select -p +``` + +You should see something like `/Library/Developer/CommandLineTools` or `/Applications/Xcode.app/Contents/Developer`. + +## Step 3: Install Homebrew + +[Homebrew](https://brew.sh) is a package manager that makes it easy to install developer tools on macOS. + +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" +``` + +Follow the prompts. When it finishes, it will tell you to run one or two commands to add Homebrew to your PATH. **Run those commands** — they look something like: + +```bash +echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile +eval "$(/opt/homebrew/bin/brew shellenv)" +``` + +To verify: + +```bash +brew --version +``` + +## Step 4: Install CMake + +CMake is the build system OrcaSlicer uses. + +```bash +brew install cmake +``` + +To verify: + +```bash +cmake --version +``` + +You need version 3.13 or later (anything recent from Homebrew will work). + +## Step 5: Install Git (if needed) + +Git usually comes with the Xcode Command Line Tools. Check: + +```bash +git --version +``` + +If it's not found: + +```bash +brew install git +``` + +## Step 6: Clone the Repository + +Choose where you want the source code. For example, in your home directory: + +```bash +cd ~ +git clone https://github.com/mnott/OrcaSlicer.git +cd OrcaSlicer +``` + +If you want the ZAA feature branch: + +```bash +git checkout feature/zaa-contouring +``` + +## Step 7: Build + +The `build.sh` script handles everything. For a first-time build: + +```bash +chmod +x build.sh +./build.sh --full +``` + +This does three things: +1. **Builds dependencies** (Boost, wxWidgets, OpenSSL, etc.) — takes 30-60 minutes the first time +2. **Configures CMake** — sets up the build system +3. **Builds OrcaSlicer** — compiles the application + +Go get a coffee. The dependency step only needs to run once. + +### After the first build + +For subsequent builds (after making code changes), just run: + +```bash +./build.sh +``` + +This does an incremental build — only recompiles what changed. Usually takes 1-5 minutes. + +### Other build options + +```bash +./build.sh --help # Show all options +./build.sh --clean # Clean rebuild (if something is broken) +./build.sh --deps # Rebuild dependencies only +./build.sh --configure # Reconfigure CMake only +``` + +## Step 8: Run OrcaSlicer + +After a successful build, the script prints the app location. You can run it with: + +```bash +open build/arm64/src/RelWithDebInfo/OrcaSlicer.app +``` + +Or find `OrcaSlicer.app` in Finder and double-click it. + +## Troubleshooting + +### "CMake not found" + +Make sure Homebrew is in your PATH (see Step 3), then `brew install cmake`. + +### "No rule to make target" or CMake generator errors + +Your build directory may have been configured with a different generator. Clean and reconfigure: + +```bash +./build.sh --clean --full +``` + +### Build fails during dependencies + +Some deps need a lot of memory. Close other applications and try again. If a specific dependency fails, check if you have the latest Xcode Command Line Tools: + +```bash +softwareupdate --list +``` + +### wxWidgets "hardcoded path" errors + +wxWidgets bakes the installation path into its config files. If you move the source directory after building deps, you need to rebuild them: + +```bash +./build.sh --deps +./build.sh --clean +``` + +### "Permission denied" on build.sh + +```bash +chmod +x build.sh +``` + +### Apple Silicon (M1/M2/M3/M4) vs Intel + +The build script auto-detects your architecture. If you need to specify it explicitly: + +```bash +./build.sh --full --arch arm64 # Apple Silicon +./build.sh --full --arch x86_64 # Intel +``` + +## Project Structure + +``` +OrcaSlicer/ +├── src/ +│ ├── libslic3r/ # Core slicing engine +│ │ ├── ContourZ.cpp # ZAA raycasting algorithm +│ │ └── ... +│ └── slic3r/GUI/ # User interface +├── deps/ # External dependencies +├── build/ # Build output (created by build.sh) +│ └── arm64/ +│ └── src/RelWithDebInfo/ +│ └── OrcaSlicer.app +├── docs/ +│ └── ZAA.md # ZAA feature documentation +├── build.sh # Build script (this guide uses it) +├── BUILD.md # This file +└── build_release_macos.sh # Official release build script +``` + +## ZAA (Z Anti-Aliasing) + +See [docs/ZAA.md](docs/ZAA.md) for details on the Z Anti-Aliasing feature. In short: enable **Z contouring** in Print Settings > Quality to get smoother curved surfaces. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000..42332937b3 --- /dev/null +++ b/build.sh @@ -0,0 +1,207 @@ +#!/usr/bin/env bash +# OrcaSlicer Build Script (macOS) +# Handles the full build lifecycle: deps, configure, build +# +# Usage: +# ./build.sh # Incremental build (after initial setup) +# ./build.sh --full # Full build from scratch (deps + configure + build) +# ./build.sh --deps # Build dependencies only +# ./build.sh --configure # Run CMake configure only +# ./build.sh --clean # Remove build dir and rebuild +# ./build.sh --help # Show all options + +set -e +set -o pipefail + +# ── Configuration ────────────────────────────────────────────── + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ARCH="${ARCH:-$(uname -m)}" +BUILD_CONFIG="${BUILD_CONFIG:-RelWithDebInfo}" +OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET:-11.3}" +NCPU=$(sysctl -n hw.ncpu 2>/dev/null || echo 4) + +BUILD_DIR="${SCRIPT_DIR}/build/${ARCH}" +DEPS_DIR="${SCRIPT_DIR}/deps" +DEPS_BUILD_DIR="${DEPS_DIR}/build/${ARCH}" +DEPS_INSTALL_DIR="${DEPS_BUILD_DIR}/OrcaSlicer_dep" + +# CMake 4.x compatibility +CMAKE_VERSION=$(cmake --version 2>/dev/null | head -1 | sed 's/[^0-9]*\([0-9]*\).*/\1/') +CMAKE_COMPAT="" +if [ "${CMAKE_VERSION:-3}" -ge 4 ] 2>/dev/null; then + CMAKE_COMPAT="-DCMAKE_POLICY_VERSION_MINIMUM=3.5" +fi + +# ── Colors ───────────────────────────────────────────────────── + +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +info() { echo -e "${BLUE}▸${NC} $*"; } +ok() { echo -e "${GREEN}✓${NC} $*"; } +warn() { echo -e "${YELLOW}⚠${NC} $*"; } +err() { echo -e "${RED}✗${NC} $*"; } + +# ── Parse Arguments ──────────────────────────────────────────── + +DO_DEPS=false +DO_CONFIGURE=false +DO_BUILD=true +DO_CLEAN=false +DO_FULL=false + +while [[ $# -gt 0 ]]; do + case $1 in + --full) DO_FULL=true; shift ;; + --deps) DO_DEPS=true; DO_BUILD=false; shift ;; + --configure) DO_CONFIGURE=true; DO_BUILD=false; shift ;; + --clean) DO_CLEAN=true; shift ;; + --arch) ARCH="$2"; shift 2 ;; + --config) BUILD_CONFIG="$2"; shift 2 ;; + --help|-h) + echo "OrcaSlicer Build Script (macOS)" + echo "" + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Build modes:" + echo " (no flags) Incremental build (fastest, use after initial setup)" + echo " --full Full build from scratch: deps → configure → build" + echo " --deps Build dependencies only (takes 30-60 min first time)" + echo " --configure Run CMake configure only" + echo " --clean Remove build directory, then rebuild" + echo "" + echo "Options:" + echo " --arch ARCH Architecture: arm64, x86_64 (default: $(uname -m))" + echo " --config CFG Build config: Release, RelWithDebInfo, Debug" + echo " (default: RelWithDebInfo)" + echo "" + echo "Examples:" + echo " $0 --full # First-time build (do this first!)" + echo " $0 # Quick rebuild after code changes" + echo " $0 --clean # Clean rebuild if something is broken" + echo "" + echo "Prerequisites: Xcode Command Line Tools, CMake 3.13+" + echo " xcode-select --install" + echo " brew install cmake" + exit 0 + ;; + *) + err "Unknown option: $1 (use --help)" + exit 1 + ;; + esac +done + +# --full implies all steps +if [ "$DO_FULL" = true ]; then + DO_DEPS=true + DO_CONFIGURE=true + DO_BUILD=true +fi + +# ── Preflight Checks ────────────────────────────────────────── + +echo "" +echo -e "${BOLD}OrcaSlicer Build${NC}" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " Architecture: ${ARCH}" +echo " Config: ${BUILD_CONFIG}" +echo " CPU cores: ${NCPU}" +echo " CMake: $(cmake --version 2>/dev/null | head -1 || echo 'NOT FOUND')" +[ -n "$CMAKE_COMPAT" ] && echo " CMake compat: 4.x → policy 3.5" +echo "" + +# Check prerequisites +if ! command -v cmake &>/dev/null; then + err "CMake not found. Install it with: brew install cmake" + exit 1 +fi + +if ! command -v git &>/dev/null; then + err "Git not found. Install Xcode Command Line Tools: xcode-select --install" + exit 1 +fi + +if ! xcode-select -p &>/dev/null; then + err "Xcode Command Line Tools not found. Install: xcode-select --install" + exit 1 +fi + +# ── Clean ────────────────────────────────────────────────────── + +if [ "$DO_CLEAN" = true ]; then + warn "Removing build directory: ${BUILD_DIR}" + rm -rf "${BUILD_DIR}" + ok "Clean complete" + echo "" +fi + +# ── Build Dependencies ───────────────────────────────────────── + +if [ "$DO_DEPS" = true ]; then + info "Building dependencies (this takes a while the first time)..." + mkdir -p "${DEPS_INSTALL_DIR}" + cd "${DEPS_BUILD_DIR}" + + cmake "${DEPS_DIR}" \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE="${BUILD_CONFIG}" \ + -DCMAKE_OSX_ARCHITECTURES:STRING="${ARCH}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \ + ${CMAKE_COMPAT} + + cmake --build . --config "${BUILD_CONFIG}" --target deps -j"${NCPU}" + + ok "Dependencies built" + echo "" +fi + +# ── Configure ────────────────────────────────────────────────── + +if [ "$DO_CONFIGURE" = true ] || { [ "$DO_BUILD" = true ] && [ ! -f "${BUILD_DIR}/Makefile" ] && [ ! -f "${BUILD_DIR}/build.ninja" ]; }; then + info "Configuring CMake..." + mkdir -p "${BUILD_DIR}" + cd "${BUILD_DIR}" + + cmake "${SCRIPT_DIR}" \ + -G "Unix Makefiles" \ + -DCMAKE_BUILD_TYPE="${BUILD_CONFIG}" \ + -DCMAKE_OSX_ARCHITECTURES="${ARCH}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \ + ${CMAKE_COMPAT} + + ok "CMake configured" + echo "" +fi + +# ── Build ────────────────────────────────────────────────────── + +if [ "$DO_BUILD" = true ]; then + if [ ! -d "${BUILD_DIR}" ]; then + err "Build directory not found: ${BUILD_DIR}" + err "Run '$0 --full' for a first-time build" + exit 1 + fi + + info "Building OrcaSlicer..." + cd "${SCRIPT_DIR}" + + cmake --build "${BUILD_DIR}" \ + --config "${BUILD_CONFIG}" \ + --target OrcaSlicer \ + -j"${NCPU}" + + echo "" + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + ok "Build complete!" + echo "" + echo " App: ${BUILD_DIR}/src/${BUILD_CONFIG}/OrcaSlicer.app" + echo "" + echo " Run: open \"${BUILD_DIR}/src/${BUILD_CONFIG}/OrcaSlicer.app\"" + echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +fi From 97a9c9fb685898d7b53e079fa0644fbe6541c165 Mon Sep 17 00:00:00 2001 From: Matthias Nott Date: Mon, 9 Feb 2026 21:18:48 +0100 Subject: [PATCH 03/28] docs: Simplify BUILD.md steps, remove redundant git install section --- BUILD.md | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/BUILD.md b/BUILD.md index fe7cb24847..6d5920c6c0 100644 --- a/BUILD.md +++ b/BUILD.md @@ -27,9 +27,10 @@ To verify it worked: ```bash xcode-select -p +git --version ``` -You should see something like `/Library/Developer/CommandLineTools` or `/Applications/Xcode.app/Contents/Developer`. +You should see a path like `/Library/Developer/CommandLineTools` and a git version. Both git and the C++ compiler are included in the Command Line Tools. ## Step 3: Install Homebrew @@ -68,21 +69,7 @@ cmake --version You need version 3.13 or later (anything recent from Homebrew will work). -## Step 5: Install Git (if needed) - -Git usually comes with the Xcode Command Line Tools. Check: - -```bash -git --version -``` - -If it's not found: - -```bash -brew install git -``` - -## Step 6: Clone the Repository +## Step 5: Clone the Repository Choose where you want the source code. For example, in your home directory: @@ -98,7 +85,7 @@ If you want the ZAA feature branch: git checkout feature/zaa-contouring ``` -## Step 7: Build +## Step 6: Build The `build.sh` script handles everything. For a first-time build: @@ -133,7 +120,7 @@ This does an incremental build — only recompiles what changed. Usually takes 1 ./build.sh --configure # Reconfigure CMake only ``` -## Step 8: Run OrcaSlicer +## Step 7: Run OrcaSlicer After a successful build, the script prints the app location. You can run it with: From 90cf80a1f863d089bc91785b4a131ea803cb5531 Mon Sep 17 00:00:00 2001 From: Matthias Nott Date: Mon, 9 Feb 2026 22:19:31 +0100 Subject: [PATCH 04/28] fix: Correct Polyline3::split_at point insertion order The split point was being prepended (append_before) to p1 instead of appended, causing it to be placed at the start of the segment rather than the end. This resulted in rogue extrusion jumps (stringing) across the model during ZAA sloped extrusions. Also adds missing append/append_before calls in the exact-point (index != -1) branch, which previously lost the split point entirely. --- src/libslic3r/Polyline.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 8ed180c72d..efbc9dfb7b 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -825,10 +825,12 @@ void Polyline3::split_at(Point &point, Polyline3* p1, Polyline3* p2) const { index = this->find_point(p); if (index != -1) { this->split_at_index(index, p1, p2); + p1->append(Point3(point, p1->last_point().z())); + p2->append_before(Point3(point, p2->first_point().z())); } else { Polyline3 temp; this->split_at_index(line_idx, p1, &temp); - p1->append_before(Point3(point, p1->last_point().z())); + p1->append(Point3(point, p1->last_point().z())); this->split_at_index(line_idx + 1, &temp, p2); p2->append_before(Point3(point, p2->first_point().z())); } From d5b4d266c732817cd0584cef7f984754ac0bf011 Mon Sep 17 00:00:00 2001 From: Matthias Nott Date: Mon, 9 Feb 2026 22:19:39 +0100 Subject: [PATCH 05/28] fix: Eliminate redundant Z coordinates in ZAA gcode output extrude_to_xyz() unconditionally emitted Z on every extrusion move via emit_xyz(), even when Z hadn't changed. With ZAA enabled, this produced ~987K redundant Z coordinates (98.2% of all Z emissions), causing 10-20% file bloat. Now compares quantized Z values before and after, and only emits Z when it actually differs at export precision (0.001mm). Reduces ZAA gcode Z emissions from 1,038,468 to 50,022 while preserving all legitimate contouring Z changes. --- src/libslic3r/GCodeWriter.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index b208148c2b..b576eacdb2 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -911,6 +911,11 @@ std::string GCodeWriter::extrude_arc_to_xy(const Vec2d& point, const Vec2d& cent std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment, bool force_no_extrusion) { + // Check if Z actually changes (at export precision) before emitting it. + // ZAA sloped extrusions call this for every segment, but many consecutive + // segments share the same quantized Z — emitting it every time is redundant. + bool z_changed = (GCodeG1Formatter::quantize_xyzf(point(2)) != GCodeG1Formatter::quantize_xyzf(m_pos(2))); + m_pos = point; m_lifted = 0; if (!force_no_extrusion) @@ -920,7 +925,10 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std Vec3d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset, point(2) }; GCodeG1Formatter w; - w.emit_xyz(point_on_plate); + if (z_changed) + w.emit_xyz(point_on_plate); + else + w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y())); if (!force_no_extrusion) w.emit_e(filament()->E()); //BBS From 285d040e4a32cbc906df30e70b4740f1711a09aa Mon Sep 17 00:00:00 2001 From: Matthias Nott Date: Mon, 9 Feb 2026 21:35:45 +0100 Subject: [PATCH 06/28] fix: Exclude external spool from 16-material AMS limit External spools (virtual trays) are not AMS slots and should not count toward the firmware's 16-material limit. Fixes blocking error when using 4 AMS units (16 slots) plus an external spool (17 total). Fixes #12135 --- src/slic3r/GUI/SelectMachine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index e0e9ec3b79..a2c0810425 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -3305,7 +3305,7 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) show_status(PrintDialogStatus::PrintStatusNoSdcard); return; } - if (wxGetApp().preset_bundle->filament_presets.size() > 16 && m_print_type != PrintFromType::FROM_SDCARD_VIEW) { + if (wxGetApp().preset_bundle->filament_presets.size() > (16 + obj_->vt_slot.size()) && m_print_type != PrintFromType::FROM_SDCARD_VIEW) { if (!obj_->is_enable_ams_np && !obj_->is_enable_np) { show_status(PrintDialogStatus::PrintStatusColorQuantityExceed); From 52d33853cbf3e3f049db625603f58ec9b35518b0 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Wed, 11 Mar 2026 00:12:21 -0700 Subject: [PATCH 07/28] Remove references to "/nonplanar" resource directory. --- src/slic3r/GUI/MainFrame.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f0fdd9af94..bec14070eb 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2454,17 +2454,6 @@ void MainFrame::init_menubar_as_editor() open_recent_project(file_id, filename); }, wxID_FILE1, wxID_FILE1 + 49); // [5050, 5100) - std::vector non_planar_projects; - for (auto &&entry : fs::directory_iterator(resources_dir() + "/nonplanar")) { - if (fs::is_regular_file(entry) && entry.path().extension() == ".3mf") { - non_planar_projects.push_back(entry.path().string()); - } - } - std::sort(non_planar_projects.begin(), non_planar_projects.end()); - for (auto &&path : non_planar_projects) { - m_recent_projects.AddFileToHistory(from_u8(path)); - } - std::vector recent_projects = wxGetApp().app_config->get_recent_projects(); std::reverse(recent_projects.begin(), recent_projects.end()); for (const std::string& project : recent_projects) From e8357f60d55130d639a2af275c2da7066f0642b3 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Wed, 11 Mar 2026 00:28:14 -0700 Subject: [PATCH 08/28] Fix ZAA test failure in test_extrusion_entity.cpp --- src/libslic3r/Point.hpp | 3 +++ tests/fff_print/test_extrusion_entity.cpp | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index f75bbb9a46..bde79076ef 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -280,6 +280,9 @@ public: Point3(const Point3 &rhs) { *this = rhs; } explicit Point3(const Point &rhs, coord_t z = 0) : Vec3crd(rhs.x(), rhs.y(), z) {} explicit Point3(const Vec3crd &vec3crd) : Vec3crd(vec3crd) {} + // This constructor allows you to construct Point from Eigen expressions + template + explicit Point3(const Eigen::MatrixBase &other) : Vec3crd(other) {} static Point3 new_scale(coordf_t x, coordf_t y, coordf_t z) { return Point3(coord_t(scale_(x)), coord_t(scale_(y)), coord_t(scale_(z))); diff --git a/tests/fff_print/test_extrusion_entity.cpp b/tests/fff_print/test_extrusion_entity.cpp index 4c41c2dc8d..f568825baf 100644 --- a/tests/fff_print/test_extrusion_entity.cpp +++ b/tests/fff_print/test_extrusion_entity.cpp @@ -11,18 +11,19 @@ using namespace Slic3r; -static inline Slic3r::Point random_point(float LO=-50, float HI=50) +static inline Slic3r::Point3 random_point3(float LO=-50, float HI=50) { - Vec2f pt = Vec2f(LO, LO) + (Vec2d(rand(), rand()) * (HI-LO) / RAND_MAX).cast(); - return pt.cast(); + Vec3f pt = Vec3f(LO, LO, LO) + (Vec3d(rand(), rand(), rand()) * (HI-LO) / RAND_MAX).cast(); + return Point3(pt.cast()); } + // build a sample extrusion entity collection with random start and end points. static Slic3r::ExtrusionPath random_path(size_t length = 20, float LO = -50, float HI = 50) { ExtrusionPath t {erPerimeter, 1.0, 1.0, 1.0}; for (size_t j = 0; j < length; ++ j) - t.polyline.append(random_point(LO, HI)); + t.polyline.append(random_point3(LO, HI)); return t; } From 4c493dd0d397ea1cecdd54eccb4e5db35c087306 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Wed, 11 Mar 2026 01:18:54 -0700 Subject: [PATCH 09/28] Remove reference to nonplanar directory from .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2318dce4de..867b49420f 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,3 @@ test.js /.cache/ .clangd internal_docs/ -resources/nonplanar/ From cf135c6843b10241e42d9084b21e13f6543764c5 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Wed, 11 Mar 2026 02:36:12 -0700 Subject: [PATCH 10/28] Revert "fix: Exclude external spool from 16-material AMS limit" This reverts commit 285d040e4a32cbc906df30e70b4740f1711a09aa. --- src/slic3r/GUI/SelectMachine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 91ba25cb51..464a1332c2 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -3305,7 +3305,7 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) show_status(PrintDialogStatus::PrintStatusNoSdcard); return; } - if (wxGetApp().preset_bundle->filament_presets.size() > (16 + obj_->vt_slot.size()) && m_print_type != PrintFromType::FROM_SDCARD_VIEW) { + if (wxGetApp().preset_bundle->filament_presets.size() > 16 && m_print_type != PrintFromType::FROM_SDCARD_VIEW) { if (!obj_->is_enable_ams_np && !obj_->is_enable_np) { show_status(PrintDialogStatus::PrintStatusColorQuantityExceed); From deb464854aa63bd3870a204847ae0b4a8fdff8f4 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Wed, 11 Mar 2026 02:47:32 -0700 Subject: [PATCH 11/28] Remove obsolete build and documentation files and clean up commented-out code --- BUILD.md | 202 ------------------------------------ build.sh | 207 ------------------------------------- docs/ZAA.md | 41 -------- src/libslic3r/ContourZ.cpp | 122 ++-------------------- version.inc | 1 - 5 files changed, 11 insertions(+), 562 deletions(-) delete mode 100644 BUILD.md delete mode 100755 build.sh delete mode 100644 docs/ZAA.md diff --git a/BUILD.md b/BUILD.md deleted file mode 100644 index 6d5920c6c0..0000000000 --- a/BUILD.md +++ /dev/null @@ -1,202 +0,0 @@ -# Building OrcaSlicer with ZAA on macOS - -A step-by-step guide to building OrcaSlicer from source on macOS. No prior experience with building software required. - -## What You'll Need - -- A Mac running macOS 11.3 (Big Sur) or later -- About 15 GB of free disk space -- An internet connection -- About 1-2 hours for the first build (mostly waiting) - -## Step 1: Open Terminal - -Press **Cmd + Space**, type **Terminal**, and press Enter. A window with a command prompt will appear. All commands below are typed into this window. - -## Step 2: Install Xcode Command Line Tools - -This installs the compiler and basic development tools. - -```bash -xcode-select --install -``` - -A dialog will pop up. Click **Install** and wait for it to finish (a few minutes). - -To verify it worked: - -```bash -xcode-select -p -git --version -``` - -You should see a path like `/Library/Developer/CommandLineTools` and a git version. Both git and the C++ compiler are included in the Command Line Tools. - -## Step 3: Install Homebrew - -[Homebrew](https://brew.sh) is a package manager that makes it easy to install developer tools on macOS. - -```bash -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -``` - -Follow the prompts. When it finishes, it will tell you to run one or two commands to add Homebrew to your PATH. **Run those commands** — they look something like: - -```bash -echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile -eval "$(/opt/homebrew/bin/brew shellenv)" -``` - -To verify: - -```bash -brew --version -``` - -## Step 4: Install CMake - -CMake is the build system OrcaSlicer uses. - -```bash -brew install cmake -``` - -To verify: - -```bash -cmake --version -``` - -You need version 3.13 or later (anything recent from Homebrew will work). - -## Step 5: Clone the Repository - -Choose where you want the source code. For example, in your home directory: - -```bash -cd ~ -git clone https://github.com/mnott/OrcaSlicer.git -cd OrcaSlicer -``` - -If you want the ZAA feature branch: - -```bash -git checkout feature/zaa-contouring -``` - -## Step 6: Build - -The `build.sh` script handles everything. For a first-time build: - -```bash -chmod +x build.sh -./build.sh --full -``` - -This does three things: -1. **Builds dependencies** (Boost, wxWidgets, OpenSSL, etc.) — takes 30-60 minutes the first time -2. **Configures CMake** — sets up the build system -3. **Builds OrcaSlicer** — compiles the application - -Go get a coffee. The dependency step only needs to run once. - -### After the first build - -For subsequent builds (after making code changes), just run: - -```bash -./build.sh -``` - -This does an incremental build — only recompiles what changed. Usually takes 1-5 minutes. - -### Other build options - -```bash -./build.sh --help # Show all options -./build.sh --clean # Clean rebuild (if something is broken) -./build.sh --deps # Rebuild dependencies only -./build.sh --configure # Reconfigure CMake only -``` - -## Step 7: Run OrcaSlicer - -After a successful build, the script prints the app location. You can run it with: - -```bash -open build/arm64/src/RelWithDebInfo/OrcaSlicer.app -``` - -Or find `OrcaSlicer.app` in Finder and double-click it. - -## Troubleshooting - -### "CMake not found" - -Make sure Homebrew is in your PATH (see Step 3), then `brew install cmake`. - -### "No rule to make target" or CMake generator errors - -Your build directory may have been configured with a different generator. Clean and reconfigure: - -```bash -./build.sh --clean --full -``` - -### Build fails during dependencies - -Some deps need a lot of memory. Close other applications and try again. If a specific dependency fails, check if you have the latest Xcode Command Line Tools: - -```bash -softwareupdate --list -``` - -### wxWidgets "hardcoded path" errors - -wxWidgets bakes the installation path into its config files. If you move the source directory after building deps, you need to rebuild them: - -```bash -./build.sh --deps -./build.sh --clean -``` - -### "Permission denied" on build.sh - -```bash -chmod +x build.sh -``` - -### Apple Silicon (M1/M2/M3/M4) vs Intel - -The build script auto-detects your architecture. If you need to specify it explicitly: - -```bash -./build.sh --full --arch arm64 # Apple Silicon -./build.sh --full --arch x86_64 # Intel -``` - -## Project Structure - -``` -OrcaSlicer/ -├── src/ -│ ├── libslic3r/ # Core slicing engine -│ │ ├── ContourZ.cpp # ZAA raycasting algorithm -│ │ └── ... -│ └── slic3r/GUI/ # User interface -├── deps/ # External dependencies -├── build/ # Build output (created by build.sh) -│ └── arm64/ -│ └── src/RelWithDebInfo/ -│ └── OrcaSlicer.app -├── docs/ -│ └── ZAA.md # ZAA feature documentation -├── build.sh # Build script (this guide uses it) -├── BUILD.md # This file -└── build_release_macos.sh # Official release build script -``` - -## ZAA (Z Anti-Aliasing) - -See [docs/ZAA.md](docs/ZAA.md) for details on the Z Anti-Aliasing feature. In short: enable **Z contouring** in Print Settings > Quality to get smoother curved surfaces. diff --git a/build.sh b/build.sh deleted file mode 100755 index 42332937b3..0000000000 --- a/build.sh +++ /dev/null @@ -1,207 +0,0 @@ -#!/usr/bin/env bash -# OrcaSlicer Build Script (macOS) -# Handles the full build lifecycle: deps, configure, build -# -# Usage: -# ./build.sh # Incremental build (after initial setup) -# ./build.sh --full # Full build from scratch (deps + configure + build) -# ./build.sh --deps # Build dependencies only -# ./build.sh --configure # Run CMake configure only -# ./build.sh --clean # Remove build dir and rebuild -# ./build.sh --help # Show all options - -set -e -set -o pipefail - -# ── Configuration ────────────────────────────────────────────── - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -ARCH="${ARCH:-$(uname -m)}" -BUILD_CONFIG="${BUILD_CONFIG:-RelWithDebInfo}" -OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET:-11.3}" -NCPU=$(sysctl -n hw.ncpu 2>/dev/null || echo 4) - -BUILD_DIR="${SCRIPT_DIR}/build/${ARCH}" -DEPS_DIR="${SCRIPT_DIR}/deps" -DEPS_BUILD_DIR="${DEPS_DIR}/build/${ARCH}" -DEPS_INSTALL_DIR="${DEPS_BUILD_DIR}/OrcaSlicer_dep" - -# CMake 4.x compatibility -CMAKE_VERSION=$(cmake --version 2>/dev/null | head -1 | sed 's/[^0-9]*\([0-9]*\).*/\1/') -CMAKE_COMPAT="" -if [ "${CMAKE_VERSION:-3}" -ge 4 ] 2>/dev/null; then - CMAKE_COMPAT="-DCMAKE_POLICY_VERSION_MINIMUM=3.5" -fi - -# ── Colors ───────────────────────────────────────────────────── - -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -BLUE='\033[0;34m' -BOLD='\033[1m' -NC='\033[0m' - -info() { echo -e "${BLUE}▸${NC} $*"; } -ok() { echo -e "${GREEN}✓${NC} $*"; } -warn() { echo -e "${YELLOW}⚠${NC} $*"; } -err() { echo -e "${RED}✗${NC} $*"; } - -# ── Parse Arguments ──────────────────────────────────────────── - -DO_DEPS=false -DO_CONFIGURE=false -DO_BUILD=true -DO_CLEAN=false -DO_FULL=false - -while [[ $# -gt 0 ]]; do - case $1 in - --full) DO_FULL=true; shift ;; - --deps) DO_DEPS=true; DO_BUILD=false; shift ;; - --configure) DO_CONFIGURE=true; DO_BUILD=false; shift ;; - --clean) DO_CLEAN=true; shift ;; - --arch) ARCH="$2"; shift 2 ;; - --config) BUILD_CONFIG="$2"; shift 2 ;; - --help|-h) - echo "OrcaSlicer Build Script (macOS)" - echo "" - echo "Usage: $0 [OPTIONS]" - echo "" - echo "Build modes:" - echo " (no flags) Incremental build (fastest, use after initial setup)" - echo " --full Full build from scratch: deps → configure → build" - echo " --deps Build dependencies only (takes 30-60 min first time)" - echo " --configure Run CMake configure only" - echo " --clean Remove build directory, then rebuild" - echo "" - echo "Options:" - echo " --arch ARCH Architecture: arm64, x86_64 (default: $(uname -m))" - echo " --config CFG Build config: Release, RelWithDebInfo, Debug" - echo " (default: RelWithDebInfo)" - echo "" - echo "Examples:" - echo " $0 --full # First-time build (do this first!)" - echo " $0 # Quick rebuild after code changes" - echo " $0 --clean # Clean rebuild if something is broken" - echo "" - echo "Prerequisites: Xcode Command Line Tools, CMake 3.13+" - echo " xcode-select --install" - echo " brew install cmake" - exit 0 - ;; - *) - err "Unknown option: $1 (use --help)" - exit 1 - ;; - esac -done - -# --full implies all steps -if [ "$DO_FULL" = true ]; then - DO_DEPS=true - DO_CONFIGURE=true - DO_BUILD=true -fi - -# ── Preflight Checks ────────────────────────────────────────── - -echo "" -echo -e "${BOLD}OrcaSlicer Build${NC}" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo " Architecture: ${ARCH}" -echo " Config: ${BUILD_CONFIG}" -echo " CPU cores: ${NCPU}" -echo " CMake: $(cmake --version 2>/dev/null | head -1 || echo 'NOT FOUND')" -[ -n "$CMAKE_COMPAT" ] && echo " CMake compat: 4.x → policy 3.5" -echo "" - -# Check prerequisites -if ! command -v cmake &>/dev/null; then - err "CMake not found. Install it with: brew install cmake" - exit 1 -fi - -if ! command -v git &>/dev/null; then - err "Git not found. Install Xcode Command Line Tools: xcode-select --install" - exit 1 -fi - -if ! xcode-select -p &>/dev/null; then - err "Xcode Command Line Tools not found. Install: xcode-select --install" - exit 1 -fi - -# ── Clean ────────────────────────────────────────────────────── - -if [ "$DO_CLEAN" = true ]; then - warn "Removing build directory: ${BUILD_DIR}" - rm -rf "${BUILD_DIR}" - ok "Clean complete" - echo "" -fi - -# ── Build Dependencies ───────────────────────────────────────── - -if [ "$DO_DEPS" = true ]; then - info "Building dependencies (this takes a while the first time)..." - mkdir -p "${DEPS_INSTALL_DIR}" - cd "${DEPS_BUILD_DIR}" - - cmake "${DEPS_DIR}" \ - -G "Unix Makefiles" \ - -DCMAKE_BUILD_TYPE="${BUILD_CONFIG}" \ - -DCMAKE_OSX_ARCHITECTURES:STRING="${ARCH}" \ - -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \ - ${CMAKE_COMPAT} - - cmake --build . --config "${BUILD_CONFIG}" --target deps -j"${NCPU}" - - ok "Dependencies built" - echo "" -fi - -# ── Configure ────────────────────────────────────────────────── - -if [ "$DO_CONFIGURE" = true ] || { [ "$DO_BUILD" = true ] && [ ! -f "${BUILD_DIR}/Makefile" ] && [ ! -f "${BUILD_DIR}/build.ninja" ]; }; then - info "Configuring CMake..." - mkdir -p "${BUILD_DIR}" - cd "${BUILD_DIR}" - - cmake "${SCRIPT_DIR}" \ - -G "Unix Makefiles" \ - -DCMAKE_BUILD_TYPE="${BUILD_CONFIG}" \ - -DCMAKE_OSX_ARCHITECTURES="${ARCH}" \ - -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" \ - ${CMAKE_COMPAT} - - ok "CMake configured" - echo "" -fi - -# ── Build ────────────────────────────────────────────────────── - -if [ "$DO_BUILD" = true ]; then - if [ ! -d "${BUILD_DIR}" ]; then - err "Build directory not found: ${BUILD_DIR}" - err "Run '$0 --full' for a first-time build" - exit 1 - fi - - info "Building OrcaSlicer..." - cd "${SCRIPT_DIR}" - - cmake --build "${BUILD_DIR}" \ - --config "${BUILD_CONFIG}" \ - --target OrcaSlicer \ - -j"${NCPU}" - - echo "" - echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - ok "Build complete!" - echo "" - echo " App: ${BUILD_DIR}/src/${BUILD_CONFIG}/OrcaSlicer.app" - echo "" - echo " Run: open \"${BUILD_DIR}/src/${BUILD_CONFIG}/OrcaSlicer.app\"" - echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" -fi diff --git a/docs/ZAA.md b/docs/ZAA.md deleted file mode 100644 index dbeecc6548..0000000000 --- a/docs/ZAA.md +++ /dev/null @@ -1,41 +0,0 @@ -# Z Anti-Aliasing (ZAA) — Z Contouring - -ZAA eliminates stair-stepping on curved and sloped top surfaces by adjusting the Z height of each extrusion point to follow the actual 3D model surface. - -Instead of printing flat horizontal layers, ZAA raycasts each point of the toolpath against the original mesh and micro-adjusts its Z coordinate to match the true surface geometry. The result is visibly smoother surfaces on domes, chamfers, and shallow slopes — without post-processing. - -This is a port of the ZAA implementation from [BambuStudio-ZAA](https://github.com/adob/BambuStudio-ZAA) by adob. - -## Configuration - -ZAA adds five settings under **Print Settings > Quality**: - -| Setting | Type | Default | Description | -|---------|------|---------|-------------| -| `zaa_enabled` | bool | off | Master enable/disable switch | -| `zaa_min_z` | float | 0.06 mm | Minimum Z layer height; also controls the slicing plane offset | -| `zaa_minimize_perimeter_height` | float | 35° | Reduce perimeter heights on slopes below this angle (0 = disabled) | -| `zaa_dont_alternate_fill_direction` | bool | off | Keep fill direction consistent instead of alternating per layer | -| `zaa_region_disable` | bool | off | Disable ZAA for a specific print region/material | - -## How It Works - -1. The slicer slices normally, then runs a **posContouring** step on each layer. -2. `ContourZ.cpp` raycasts every extrusion point vertically against the source mesh. -3. Each point's Z is adjusted to the mesh intersection, converting flat `Polyline` paths into `Polyline3` paths that carry per-point Z coordinates. -4. The G-code writer emits the adjusted Z values, so the printer follows the true surface. - -## Key Implementation Details - -- **Core algorithm**: `src/libslic3r/ContourZ.cpp` (~330 lines) -- **3D geometry**: `Point3`, `Line3`, `Polyline3`, `MultiPoint3` extend the existing 2D types -- **Pipeline step**: `posContouring` in `PrintObject.cpp`, runs after perimeter/infill generation -- **G-code output**: `GCode.cpp` writes per-point Z when `path.z_contoured` is set -- **Arc fitting**: Templated to work with both 2D and 3D geometry -- **ExtrusionPath change**: `polyline` field changed from `Polyline` to `Polyline3` - -## Testing - -1. Load a model with curved top surfaces (spheres, domes, chamfered edges) -2. Enable **Z contouring** in Print Settings > Quality -3. Slice and inspect the G-code — Z values should vary within each layer on contoured surfaces diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp index 13a266573f..0182b35aad 100644 --- a/src/libslic3r/ContourZ.cpp +++ b/src/libslic3r/ContourZ.cpp @@ -15,49 +15,25 @@ namespace Slic3r { static void contour_extrusion_entity(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntity *extr); -// static double lowest_z_within_distance(const Vec3d &normal, double dist) { -// const Vec3d p(0.0, 0.0, 0.0); -// Eigen::Vector3d n_unit = normal.normalized(); -// Eigen::Vector3d z_hat(0.0, 0.0, 1.0); - -// // Project the negative z-direction into the tangent plane -// Eigen::Vector3d v_dir = -z_hat + (z_hat.dot(n_unit)) * n_unit; - -// double norm_v = v_dir.norm(); -// if (norm_v == 0.0) { -// // Surface is horizontal, cannot go lower in z within tangent plane -// return p.z(); -// } - -// Eigen::Vector3d v = dist * v_dir / norm_v; -// Eigen::Vector3d q = p + v; -// return q.z(); -// } - -static double follow_slope_down(double angle_rad, double dist) { +static double follow_slope_down(double angle_rad, double dist) +{ return -dist * std::sin(angle_rad); } -static double slope_from_normal(const Eigen::Vector3d& normal) { +static double slope_from_normal(const Eigen::Vector3d& normal) +{ // Ensure the normal is normalized Eigen::Vector3d n = normal.normalized(); // Compute angle between normal and z-axis double angle_rad = std::acos(std::abs(n.z())); // angle between normal and vertical return angle_rad; - - // calculate fall over dist - // double dist = 0.2; - // double z_dist = lowest_z(angle_rad, dist); - // printf("fall %f vs %f\n", z_dist, lowest_z_within_distance(normal, dist)); - - // double angle_deg = angle_rad * 180.0 / M_PI; - // return angle_deg; } // const int LINE = 180; -static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionPath &path) { +static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionPath &path) +{ if (region->region().config().zaa_region_disable) { return false; } @@ -74,19 +50,11 @@ static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh & double resolution_mm = 0.1; coordf_t height = layer->height; - // std::cout << "LAYER " << (layer->id()+1) << std::endl; - // std::cout << "PRINT Z " << layer->print_z << std::endl; - // std::cout << "LAYER HEIGHT " << height << std::endl; - // std::cout << "EXTRUSION HEIGHT " << path.height << std::endl; - // std::cout << "EXTRUSION WIDTH " << path.width << std::endl; - // std::cout << "EXTRUSION ROLE: " << ExtrusionEntity::role_to_string(path.role()) << std::endl; - // std::cout << "FIRST POINT: " << path.polyline.first_point() << std::endl; double minimize_perimeter_height_angle = region->region().config().zaa_minimize_perimeter_height; Pointf3s contoured_points; bool was_contoured = false; - // bool is_perimeter = path.role() == erExternalPerimeter || path.role() == erPerimeter || path.role() == erOverhangPerimeter; for (Points3::const_iterator it = points.begin(); it != points.end()-1; ++it) { Vec2d p1d(unscale_(it->x()), unscale_(it->y())); @@ -195,13 +163,15 @@ static void contour_extrusion_loop(LayerRegion *region, const sla::IndexedMesh & } } -static void contour_extrusion_entitiy_collection(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntityCollection &collection) { +static void contour_extrusion_entitiy_collection(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntityCollection &collection) +{ for (ExtrusionEntity *entity : collection.entities) { contour_extrusion_entity(region, mesh, entity); } } -static void contour_extrusion_entity(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntity *extr) { +static void contour_extrusion_entity(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntity *extr) +{ const ExtrusionPathSloped *sloped = dynamic_cast(extr); if (sloped != nullptr) { throw RuntimeError("ExtrusionPathSloped not implemented"); @@ -253,81 +223,11 @@ static void handle_extrusion_collection(LayerRegion *region, const sla::IndexedM } } -// static void find_point(ExtrusionPath &path, const std::string &path_info) { -// Points3 &points = path.polyline.points; - -// size_t i = 0; -// for (Points3::const_iterator it = points.begin(); it != points.end()-1; ++it) { -// if (it->x() == -883971 && it->y() == 979001) { -// std::cout << "FOUND POINT " << ExtrusionEntity::role_to_string(path.role()) << " at path " << path_info << "[" + std::to_string(i) + "]" << std::endl; -// } -// i++; -// } -// } - -// static void find_point(ExtrusionLoop &loop, const std::string &path_info) { -// size_t i = 0; -// for (ExtrusionPath &path : loop.paths) { -// find_point(path, path_info + "[" + std::to_string(i) + "]"); -// i++; -// } -// } - -// static void find_point(ExtrusionEntity &extr, const std::string &path); - -// static void find_point(ExtrusionEntityCollection &collection, const std::string &path) { -// size_t i = 0; -// for (ExtrusionEntity *extr : collection.entities) { -// find_point(*extr, path + "[" + std::to_string(i) + "]"); -// i++; -// } -// } - -// static void find_point(ExtrusionEntity &extr, const std::string &path_info) { -// const ExtrusionPathSloped *sloped = dynamic_cast(&extr); -// if (sloped != nullptr) { -// throw RuntimeError("ExtrusionPathSloped not implemented"); -// return; -// } - -// ExtrusionPath *path = dynamic_cast(&extr); -// if (path != nullptr) { -// find_point(*path, path_info + " as ExtrusionPath " + ExtrusionEntity::role_to_string(extr.role())); -// return; -// } - -// ExtrusionLoop *loop = dynamic_cast(&extr); -// if (loop != nullptr) { -// find_point(*loop, path_info + " as ExtrusionLoop " + ExtrusionEntity::role_to_string(extr.role())); -// return; -// } - -// const ExtrusionLoopSloped *loop_sloped = dynamic_cast(&extr); -// if (loop_sloped != nullptr) { -// throw RuntimeError("ExtrusionLoopSloped not implemented"); -// return; -// } - -// ExtrusionEntityCollection *collection = dynamic_cast(&extr); -// if (collection != nullptr) { -// find_point(*collection, path_info + " as ExtrusionEntityCollection " + ExtrusionEntity::role_to_string(extr.role())); -// return; -// } - -// throw RuntimeError("ContourZ: ExtrusionEntity type not implemented"); -// return; -// } - void Layer::make_contour_z(const sla::IndexedMesh &mesh) { - // printf("make_contour_z() called\n"); for (LayerRegion *region : this->regions()) { - // printf("processing layer region %p\n", region); - // find_point(region->fills, "fills"); - // find_point(region->perimeters, "perimeters"); - handle_extrusion_collection(region, mesh, region->fills, {erTopSolidInfill, erIroning, erExternalPerimeter, erMixed}); handle_extrusion_collection(region, mesh, region->perimeters, {erExternalPerimeter, erMixed}); } } -} // namespace Slic3r \ No newline at end of file +} // namespace Slic3r diff --git a/version.inc b/version.inc index 86d3759e73..d6c6b293b3 100644 --- a/version.inc +++ b/version.inc @@ -8,7 +8,6 @@ if(NOT DEFINED BBL_INTERNAL_TESTING) set(BBL_INTERNAL_TESTING "0") endif() set(SoftFever_VERSION "2.3.2-dev") -set(ZAA_VERSION "1.0.3") string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" SoftFever_VERSION_MATCH ${SoftFever_VERSION}) set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1}) From 59f6d75df762e46cd181acff87a0a98922063555 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Wed, 11 Mar 2026 16:50:52 -0700 Subject: [PATCH 12/28] Revert extraneous changes. --- src/libslic3r/libslic3r_version.h.in | 1 - src/slic3r/GUI/GUI_App.cpp | 39 +++++----------------------- src/slic3r/GUI/GUI_App.hpp | 1 - src/slic3r/GUI/MainFrame.cpp | 3 --- src/slic3r/GUI/OptionsGroup.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 7 ++--- src/slic3r/GUI/Search.cpp | 20 ++++---------- 7 files changed, 14 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/libslic3r_version.h.in b/src/libslic3r/libslic3r_version.h.in index df2a92f75b..750e092d28 100644 --- a/src/libslic3r/libslic3r_version.h.in +++ b/src/libslic3r/libslic3r_version.h.in @@ -5,7 +5,6 @@ #define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@" #define SLIC3R_VERSION "@SLIC3R_VERSION@" #define SoftFever_VERSION "@SoftFever_VERSION@" -#define ZAA_VERSION "@ZAA_VERSION@" #ifndef GIT_COMMIT_HASH #define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized #endif diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 9f08cdcbbf..157b168372 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2572,32 +2572,13 @@ void GUI_App::init_single_instance_checker(const std::string &name, const std::s m_single_instance_checker = std::make_unique(boost::nowide::widen(name), boost::nowide::widen(path)); } -bool GUI_App::CallOnInit() -{ - // Override wxApp::CallOnInit to catch exceptions from ~wxMacAutoreleasePool - try { - return wxApp::CallOnInit(); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(fatal) << "Exception in CallOnInit: " << e.what(); - return false; - } catch (...) { - // The app was initialized, just the autorelease pool cleanup threw. - // Return true to let the app continue. - return m_initialized; - } -} - bool GUI_App::OnInit() { try { return on_init_inner(); } catch (const std::exception& e) { BOOST_LOG_TRIVIAL(fatal) << "OnInit Got Fatal error: " << e.what(); - flush_logs(); - return false; - } catch (...) { - BOOST_LOG_TRIVIAL(fatal) << "OnInit caught non-std exception"; - flush_logs(); + generic_exception_handle(); return false; } } @@ -3420,16 +3401,8 @@ bool GUI_App::on_init_network(bool try_backup) Slic3r::NetworkAgentFactory::register_all_agents(); // m_agent = new Slic3r::NetworkAgent(data_directory); - try { - std::unique_ptr agent_ptr = Slic3r::create_agent_from_config(data_directory, app_config); - m_agent = agent_ptr.release(); - } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "Failed to create network agent: " << e.what(); - m_agent = nullptr; - } catch (...) { - BOOST_LOG_TRIVIAL(error) << "Failed to create network agent: unknown exception (code signing?)"; - m_agent = nullptr; - } + std::unique_ptr agent_ptr = Slic3r::create_agent_from_config(data_directory, app_config); + m_agent = agent_ptr.release(); if (!m_device_manager) m_device_manager = new Slic3r::DeviceManager(m_agent); @@ -5731,7 +5704,6 @@ std::string GUI_App::format_display_version() if (!version_display.empty()) return version_display; version_display = SoftFever_VERSION; - version_display += " / ZAA v" + std::string(ZAA_VERSION); return version_display; } @@ -6563,8 +6535,7 @@ void GUI_App::update_mode() mainframe->m_param_dialog->panel()->update_mode(); if (mainframe->m_printer_view) mainframe->m_printer_view->update_mode(); - if (mainframe->m_webview) - mainframe->m_webview->update_mode(); + mainframe->m_webview->update_mode(); #ifdef _MSW_DARK_MODE if (!wxGetApp().tabs_as_menu()) @@ -6576,6 +6547,8 @@ void GUI_App::update_mode() for (auto tab : model_tabs_list) tab->update_mode(); + //BBS plater()->update_menus(); + plater()->canvas3D()->update_gizmos_on_off_state(); } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index fadd2ae24e..ae51add264 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -324,7 +324,6 @@ public: void on_start_subscribe_again(std::string dev_id); std::string get_local_models_path(); bool OnInit() override; - bool CallOnInit() override; int OnExit() override; bool initialized() const { return m_initialized; } inline bool is_enable_multi_machine() { return this->app_config&& this->app_config->get("enable_multi_machine") == "true"; } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 74a7756b62..80b801a4c0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1,7 +1,5 @@ #include "MainFrame.hpp" -#include -#include #include #include #include @@ -25,7 +23,6 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/SLAPrint.hpp" #include "libslic3r/PresetBundle.hpp" -#include "libslic3r/Utils.hpp" #include "Tab.hpp" #include "ProgressStatusBar.hpp" diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index b1cc8804e4..534646d593 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -622,7 +622,7 @@ void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost:: Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/) { if (!m_config->has(opt_key)) { - // Option not in config — may be newly added (e.g. ZAA options) + std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.\n"; } std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 18d2c13e5c..671d5674b6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3846,7 +3846,7 @@ static std::vector get_search_inputs(ConfigOptionMode mode) auto& tabs_list = wxGetApp().tabs_list; auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology(); for (auto tab : tabs_list) - if (tab && tab->supports_printer_technology(print_tech) && tab->get_config()) + if (tab->supports_printer_technology(print_tech)) ret.emplace_back(Search::InputInfo {tab->get_config(), tab->type(), mode}); return ret; @@ -3870,9 +3870,7 @@ void Sidebar::update_mode() //obj_list()->get_sizer()->Show(m_mode > comSimple); obj_list()->unselect_objects(); - // Guard: during startup the 3D canvas selection may not be fully initialized - if (wxGetApp().initialized()) - obj_list()->update_selections(); + obj_list()->update_selections(); // obj_list()->update_object_menu(); Layout(); @@ -10417,7 +10415,6 @@ void Plater::priv::set_project_name(const wxString& project_name) { BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << __LINE__ << " project is:" << project_name; m_project_name = project_name; - wxString name = project_name + " - OrcaSlicer-ZAA"; //update topbar title #ifdef __APPLE__ wxGetApp().mainframe->SetTitle(m_project_name); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index db885c4310..1aef329297 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -70,7 +70,6 @@ static std::string get_key(const std::string &opt_key, Preset::Type type) { retu void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type type, ConfigOptionMode mode) { - if (!config) return; auto emplace = [this, type](const std::string key, const wxString &label) { const GroupAndCategory &gc = groups_and_categories[key]; if (gc.group.IsEmpty() || gc.category.IsEmpty()) return; @@ -90,19 +89,13 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty }; for (std::string opt_key : config->keys()) { - auto def_it = config->def()->options.find(opt_key); - if (def_it == config->def()->options.end()) { - continue; - } - const ConfigOptionDef &opt = def_it->second; + const ConfigOptionDef &opt = config->def()->options.at(opt_key); if (opt.mode > mode) continue; int cnt = 0; - if ((type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER || type == Preset::TYPE_PRINT) && opt_key != "printable_area") { - const ConfigOption *opt_ptr = config->option(opt_key); - if (!opt_ptr) continue; - switch (opt_ptr->type()) { + if ((type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER) && opt_key != "printable_area") + switch (config->option(opt_key)->type()) { case coInts: change_opt_key(opt_key, config, cnt); break; case coBools: change_opt_key(opt_key, config, cnt); break; case coFloats: change_opt_key(opt_key, config, cnt); break; @@ -113,7 +106,6 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty case coEnums: change_opt_key(opt_key, config, cnt); break; default: break; } - } wxString label = opt.full_label.empty() ? opt.label : opt.full_label; @@ -230,7 +222,7 @@ bool OptionsSearcher::search(const std::string &search, bool force /* = false*/, if (full_list) { std::string label = into_u8(get_label(opt)); //all - if (type == Preset::TYPE_INVALID) { + if (type == Preset::TYPE_INVALID) { found.emplace_back(FoundOption{label, label, into_u8(get_tooltip(opt)), i, 0}); } else if (type == opt.type){ found.emplace_back(FoundOption{label, label, into_u8(get_tooltip(opt)), i, 0}); @@ -297,9 +289,7 @@ OptionsSearcher::~OptionsSearcher() {} void OptionsSearcher::init(std::vector input_values) { options.clear(); - for (size_t idx = 0; idx < input_values.size(); ++idx) { - append_options(input_values[idx].config, input_values[idx].type, input_values[idx].mode); - } + for (auto i : input_values) append_options(i.config, i.type, i.mode); sort_options(); search(search_line, true, search_type); From 9fae402d62c9b21fbc5f494559f9b40da301e172 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Fri, 13 Mar 2026 01:56:51 -0700 Subject: [PATCH 13/28] Fix scarf seams --- src/libslic3r/ArcFitter.cpp | 4 +- src/libslic3r/ExtrusionEntity.cpp | 2 +- src/libslic3r/GCode.cpp | 24 +-- src/libslic3r/GCode.hpp | 17 +- src/libslic3r/GCode/SeamPlacer.cpp | 6 +- src/libslic3r/LayerRegion.cpp | 1 + src/libslic3r/Line.hpp | 5 + src/libslic3r/MultiPoint.cpp | 129 ++++++------ src/libslic3r/MultiPoint.hpp | 8 +- src/libslic3r/PerimeterGenerator.cpp | 21 +- src/libslic3r/Point.cpp | 14 +- src/libslic3r/Point.hpp | 9 +- src/libslic3r/Polyline.cpp | 281 +++++++++++++++++++++------ src/libslic3r/Polyline.hpp | 15 +- 14 files changed, 368 insertions(+), 168 deletions(-) diff --git a/src/libslic3r/ArcFitter.cpp b/src/libslic3r/ArcFitter.cpp index b15548ee84..1d48861e0c 100644 --- a/src/libslic3r/ArcFitter.cpp +++ b/src/libslic3r/ArcFitter.cpp @@ -85,7 +85,7 @@ static void do_arc_fitting_tmpl(const POINTS& points, std::vectorpaths.size() == 1) { if (!p1.polyline.is_valid()) { std::swap(this->paths.front().polyline.points, p2.polyline.points); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6a64a2ed2d..4c08a2e7a4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -5541,7 +5541,11 @@ static std::unique_ptr calculate_layer_edge_grid(const Layer& la return out; } -std::string GCode::extrude_loop(const ExtrusionLoop &loop_ref, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters, const Point* start_point) +std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref, + const std::string& description, + double speed, + const ExtrusionEntitiesPtr& region_perimeters, + const Point* start_point) { // get a copy; don't modify the orientation of the original loop object otherwise // next copies (if any) would not detect the correct orientation @@ -5707,7 +5711,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &loop_ref, std::string descr if (!enable_seam_slope) { for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { gcode += this->_extrude(*path, description, speed_for_path(*path)); - // Orca: Adaptive PA - dont adapt PA after the first pultipath extrusion is completed + // Orca: Adaptive PA - dont adapt PA after the first multipath extrusion is completed // as we have already set the PA value to the average flow over the totality of the path // in the first extrude move // TODO: testing is needed with slope seams and adaptive PA. @@ -5819,7 +5823,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop &loop_ref, std::string descr return gcode; } -std::string GCode::extrude_multi_path(const ExtrusionMultiPath &multipath, std::string description, double speed) +std::string GCode::extrude_multi_path(const ExtrusionMultiPath& multipath, const std::string& description, double speed) { // extrude along the path std::string gcode; @@ -5868,7 +5872,10 @@ std::string GCode::extrude_multi_path(const ExtrusionMultiPath &multipath, std:: return gcode; } -std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters) +std::string GCode::extrude_entity(const ExtrusionEntity& entity, + const std::string& description, + double speed, + const ExtrusionEntitiesPtr& region_perimeters) { if (const ExtrusionPath* path = dynamic_cast(&entity)) return this->extrude_path(*path, description, speed); @@ -5881,7 +5888,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des return ""; } -std::string GCode::extrude_path(const ExtrusionPath &path, std::string description, double speed) +std::string GCode::extrude_path(const ExtrusionPath& path, const std::string& description, double speed) { // Orca: Reset average multipath flow as this is a single line, single extrude volumetric speed path m_multi_flow_segment_path_pa_set = false; @@ -6155,12 +6162,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, throw RuntimeError("GCode: very low z"); } } - gcode += this->travel_to( - path.first_point(), - path.role(), - "move to first " + description + " point; size " + std::to_string(path.polyline.size()), - z - ); + gcode += this->travel_to(path.first_point(), path.role(), "move to first " + description + " point", z); m_need_change_layer_lift_z = false; // Orca: ensure Z matches planned layer height if (_last_pos_undefined && !slope_need_z_travel) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 8d877264f6..29ef4a0dc8 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -390,13 +390,20 @@ private: std::string change_layer(coordf_t print_z); // Orca: pass the complete collection of region perimeters to the extrude loop to check whether the wipe before external loop // should be executed - std::string extrude_entity(const ExtrusionEntity &entity, std::string description = "", double speed = -1., const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr()); + std::string extrude_entity(const ExtrusionEntity& entity, + const std::string& description = "", + double speed = -1., + const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr()); // Orca: pass the complete collection of region perimeters to the extrude loop to check whether the wipe before external loop // should be executed - std::string extrude_loop(const ExtrusionLoop &loop, std::string description, double speed = -1., const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr(), const Point* start_point = nullptr); - std::string extrude_multi_path(const ExtrusionMultiPath &multipath, std::string description = "", double speed = -1.); - std::string extrude_path(const ExtrusionPath &path, std::string description = "", double speed = -1.); - + std::string extrude_loop(const ExtrusionLoop& loop, + const std::string& description, + double speed = -1., + const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr(), + const Point* start_point = nullptr); + std::string extrude_multi_path(const ExtrusionMultiPath& multipath, const std::string& description = "", double speed = -1.); + std::string extrude_path(const ExtrusionPath& path, const std::string& description = "", double speed = -1.); + // Orca: Adaptive PA variables // Used for adaptive PA when extruding paths with multiple, varying flow segments. // This contains the sum of the mm3_per_mm values weighted by the length of each path segment. diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 12f19f6fa8..5e39ee9f46 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1514,8 +1514,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, current.path_idx = next_idx_modulo(current.path_idx, loop.paths.size()); current.segment_idx = 0; } - const Point3 &p3 = loop.paths[current.path_idx].polyline.points[current.segment_idx]; - current.foot_pt = Point(p3.x(), p3.y()); + current.foot_pt = loop.paths[current.path_idx].polyline.points[current.segment_idx].to_point(); return current; }; @@ -1528,8 +1527,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, size_t closest_perimeter_point_index = 0; { // local space for the closest_perimeter_point_index Perimeter *closest_perimeter = nullptr; - const Point3 &init_p3 = loop.paths[0].polyline.points[0]; - ExtrusionLoop::ClosestPathPoint closest_point{0,0,Point(init_p3.x(), init_p3.y())}; + ExtrusionLoop::ClosestPathPoint closest_point{0, 0, loop.paths[0].polyline.points[0].to_point()}; size_t points_count = std::accumulate(loop.paths.begin(), loop.paths.end(), 0, [](size_t acc,const ExtrusionPath& p) { return acc + p.polyline.points.size(); }); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index ec88905b96..fdf03570a8 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -3,6 +3,7 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "PerimeterGenerator.hpp" +#include "Point.hpp" #include "Print.hpp" #include "Surface.hpp" #include "BoundingBox.hpp" diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 28423b0dfc..99935b1fc4 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -236,6 +236,11 @@ public: // Convert to 2D line by dropping Z coordinate Line to_line() const { return Line(this->a.to_point(), this->b.to_point()); } + static inline double distance_to_squared(const Point3& point, const Point3& a, const Point3& b) + { + return line_alg::distance_to_squared(Line3{a, b}, Vec<3, coord_t>{point}); + } + Point3 a; Point3 b; diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index cb9c67f0f9..694d3ea9d0 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -422,32 +422,23 @@ Points MultiPoint::concave_hull_2d(const Points& pts, const double tolerence) return min_distance; } - -void MultiPoint3::translate(double x, double y) +void MultiPoint3::translate(const Point3& v) { - for (Vec3crd &p : points) { - p(0) += coord_t(x); - p(1) += coord_t(y); - } -} - -void MultiPoint3::translate(const Point& vector) -{ - this->translate(vector(0), vector(1)); + for (Point3& pt : points) + pt += v; } double MultiPoint3::length() const { - double len = 0.0; - for (const Line3& line : this->lines()) - len += line.length(); + const Lines3& lines = this->lines(); + double len = 0; + for (auto it = lines.cbegin(); it != lines.cend(); ++it) { + len += it->length(); + } return len; } -BoundingBox3 MultiPoint3::bounding_box() const -{ - return BoundingBox3(points); -} +BoundingBox3 MultiPoint3::bounding_box() const { return BoundingBox3(this->points); } bool MultiPoint3::remove_duplicate_points() { @@ -472,48 +463,72 @@ bool MultiPoint3::remove_duplicate_points() } // Douglas-Peucker simplification for 3D points -Points3 MultiPoint3::_douglas_peucker(const Points3 &points, double tolerance) +Points3 MultiPoint3::_douglas_peucker(const Points3& pts, double tolerance) { - if (points.size() <= 2) return points; - - // Find the point with maximum distance from line segment - double max_dist = 0; - size_t max_idx = 0; - const Point3 &first = points.front(); - const Point3 &last = points.back(); - Line3 line(first, last); - - for (size_t i = 1; i < points.size() - 1; ++i) { - // Calculate perpendicular distance to line segment - Point3 proj = points[i].projection_onto(line); - double dist = points[i].distance_to(proj); - if (dist > max_dist) { - max_dist = dist; - max_idx = i; + Points3 result_pts; + double tolerance_sq = tolerance * tolerance; + if (!pts.empty()) { + const Point3* anchor = &pts.front(); + size_t anchor_idx = 0; + const Point3* floater = &pts.back(); + size_t floater_idx = pts.size() - 1; + result_pts.reserve(pts.size()); + result_pts.emplace_back(*anchor); + if (anchor_idx != floater_idx) { + assert(pts.size() > 1); + std::vector dpStack; + dpStack.reserve(pts.size()); + dpStack.emplace_back(floater_idx); + for (;;) { + double max_dist_sq = 0.0; + size_t furthest_idx = anchor_idx; + // find point furthest from line seg created by (anchor, floater) and note it + for (size_t i = anchor_idx + 1; i < floater_idx; ++i) { + double dist_sq = Line3::distance_to_squared(pts[i], *anchor, *floater); + if (dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest_idx = i; + } + } + // remove point if less than tolerance + if (max_dist_sq <= tolerance_sq) { + result_pts.emplace_back(*floater); + anchor_idx = floater_idx; + anchor = floater; + assert(dpStack.back() == floater_idx); + dpStack.pop_back(); + if (dpStack.empty()) + break; + floater_idx = dpStack.back(); + } else { + floater_idx = furthest_idx; + dpStack.emplace_back(floater_idx); + } + floater = &pts[floater_idx]; + } } + assert(result_pts.front() == pts.front()); + assert(result_pts.back() == pts.back()); + +#if 0 + { + static int iRun = 0; + BoundingBox bbox(pts); + BoundingBox bbox2(result_pts); + bbox.merge(bbox2); + SVG svg(debug_out_path("douglas_peucker_%d.svg", iRun ++).c_str(), bbox); + if (pts.front() == pts.back()) + svg.draw(Polygon(pts), "black"); + else + svg.draw(Polyline(pts), "black"); + if (result_pts.front() == result_pts.back()) + svg.draw(Polygon(result_pts), "green", scale_(0.1)); + else + svg.draw(Polyline(result_pts), "green", scale_(0.1)); + } +#endif } - - // If max distance is greater than tolerance, recursively simplify - if (max_dist > tolerance) { - // Recursive call for first part - Points3 left_points(points.begin(), points.begin() + max_idx + 1); - Points3 left_result = _douglas_peucker(left_points, tolerance); - - // Recursive call for second part - Points3 right_points(points.begin() + max_idx, points.end()); - Points3 right_result = _douglas_peucker(right_points, tolerance); - - // Concatenate results (avoiding duplicate middle point) - Points3 result = left_result; - result.insert(result.end(), right_result.begin() + 1, right_result.end()); - return result; - } else { - // All points between first and last can be removed - Points3 result; - result.push_back(first); - result.push_back(last); - return result; - } + return result_pts; } BoundingBox get_extents(const MultiPoint &mp) diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 44d1e86cf1..de386b501c 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -121,9 +121,13 @@ public: void append(const Point3& point) { this->points.push_back(point); } void append(const Vec3crd& point) { this->points.push_back(Point3(point)); } + void append(const Points3::const_iterator& begin, const Points3::const_iterator& end) + { + this->points.insert(this->points.end(), begin, end); + } - void translate(double x, double y); - void translate(const Point& vector); + void translate(double x, double y, double z = 0) { this->translate(Point3(coord_t(x), coord_t(y), coord_t(z))); } + void translate(const Point3& vector); void reverse() { std::reverse(this->points.begin(), this->points.end()); } void rotate(double angle) { this->rotate(cos(angle), sin(angle)); } void rotate(double cos_angle, double sin_angle); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index b7cb3e34ef..e16a0b022a 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -464,10 +464,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p }; std::unordered_map point_occurrence; for (const ExtrusionPath& path : paths) { - const Point3 &first_p3 = path.polyline.first_point(); - const Point3 &last_p3 = path.polyline.last_point(); - Point first_p = Point(first_p3.x(), first_p3.y()); - Point last_p = Point(last_p3.x(), last_p3.y()); + Point first_p = path.polyline.first_point().to_point(); + Point last_p = path.polyline.last_point().to_point(); ++point_occurrence[first_p].occurrence; ++point_occurrence[last_p].occurrence; if (path.role() == erOverhangPerimeter) { @@ -660,13 +658,17 @@ bool paths_touch(const ExtrusionPath &path_one, const ExtrusionPath &path_two, d { AABBTreeLines::LinesDistancer lines_two{path_two.as_polyline().lines()}; for (size_t pt_idx = 0; pt_idx < path_one.polyline.size(); pt_idx++) { - const Point3 &p3 = path_one.polyline.points[pt_idx]; - if (lines_two.distance_from_lines(Point(p3.x(), p3.y())) < limit_distance) { return true; } + Point pt = path_one.polyline.points[pt_idx].to_point(); + if (lines_two.distance_from_lines(pt) < limit_distance) { + return true; + } } AABBTreeLines::LinesDistancer lines_one{path_one.as_polyline().lines()}; for (size_t pt_idx = 0; pt_idx < path_two.polyline.size(); pt_idx++) { - const Point3 &p3 = path_two.polyline.points[pt_idx]; - if (lines_one.distance_from_lines(Point(p3.x(), p3.y())) < limit_distance) { return true; } + Point pt = path_two.polyline.points[pt_idx].to_point(); + if (lines_one.distance_from_lines(pt) < limit_distance) { + return true; + } } return false; } @@ -1032,8 +1034,7 @@ std::tuple, Polygons> generate_extra_perimeters_over size_t min_dist_idx = 0; double min_dist = std::numeric_limits::max(); for (size_t i = 0; i < overhang_region.front().polyline.size(); i++) { - const Point3 &p3 = overhang_region.front().polyline.points[i]; - Point p = Point(p3.x(), p3.y()); + Point p = overhang_region.front().polyline.points[i].to_point(); if (double d = lower_layer_aabb_tree.distance_from_lines(p) < min_dist) { min_dist = d; min_dist_idx = i; diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp index eea69b1195..9a2c2b14cd 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -1,4 +1,5 @@ #include "Point.hpp" +#include "Exception.hpp" #include "Line.hpp" #include "MultiPoint.hpp" #include "Polyline.hpp" @@ -266,11 +267,21 @@ Points to_points(const Points3 &points3) { Points points2; points2.reserve(points3.size()); for (const Point3 &pt : points3) { - points2.push_back(pt.to_point()); + points2.emplace_back(pt.to_point()); } return points2; } +Points3 to_points3(const Points& points) +{ + Points3 points3; + points3.reserve(points.size()); + for (const Point& pt : points) { + points3.emplace_back(pt); + } + return points3; +} + // Point3 method implementations void Point3::rotate(double angle, const Point3 ¢er) { Vec3crd diff = *this - center; @@ -311,6 +322,7 @@ double Point3::ccw_angle(const Point3 &p1, const Point3 &p2) const { Point3 Point3::projection_onto(const MultiPoint3 &poly) const { // TODO: Implement proper 3D projection when MultiPoint3 conversion methods are ready // For now, stub implementation + throw RuntimeError("Point3::projection_onto(MultiPoint3) not implemented yet"); return *this; } diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index bde79076ef..83945d73ce 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -62,7 +62,7 @@ using PointsAllocator = tbb::scalable_allocator; using Points = std::vector>; using PointPtrs = std::vector; using PointConstPtrs = std::vector; -using Points3 = std::vector; +using Points3 = std::vector>; using Pointfs = std::vector; using Vec2ds = std::vector; using Pointf3s = std::vector; @@ -88,6 +88,7 @@ using Transform3d = Eigen::Transform; // I don't know why Eigen::Transform::Identity() return a const object... @@ -422,6 +423,12 @@ inline Point lerp(const Point &a, const Point &b, double t) return ((1. - t) * a.cast() + t * b.cast()).cast(); } +inline Point3 lerp(const Point3& a, const Point3& b, double t) +{ + assert((t >= -EPSILON) && (t <= 1. + EPSILON)); + return Point3(((1. - t) * a.cast() + t * b.cast()).cast()); +} + // if IncludeBoundary, then a bounding box is defined even for a single point. // otherwise a bounding box is only defined if it has a positive area. template diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index efbc9dfb7b..98a5366ecf 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -661,19 +661,17 @@ void ThickPolyline::start_at_index(int index) Lines3 Polyline3::lines() const { Lines3 lines; - if (points.size() >= 2) - { - lines.reserve(points.size() - 1); - for (Points3::const_iterator it = points.begin(); it != points.end() - 1; ++it) - { + if (this->points.size() >= 2) { + lines.reserve(this->points.size() - 1); + for (Points3::const_iterator it = this->points.begin(); it != this->points.end() - 1; ++it) { lines.emplace_back(*it, *(it + 1)); } } return lines; } -// Polyline3 ZAA methods implementation -Polyline Polyline3::to_polyline() const { +Polyline Polyline3::to_polyline() const +{ Polyline out; out.points.reserve(this->points.size()); for (const Point3 &point : this->points) { @@ -682,8 +680,10 @@ Polyline Polyline3::to_polyline() const { return out; } -void Polyline3::clip_end(double distance) { - size_t remove_after_index = this->size(); +void Polyline3::clip_end(double distance) +{ + bool last_point_inserted = false; + size_t remove_after_index = MultiPoint3::size(); while (distance > 0) { Vec3d last_point = this->last_point().cast(); this->points.pop_back(); @@ -692,35 +692,66 @@ void Polyline3::clip_end(double distance) { this->fitting_result.clear(); return; } - Vec3d v = this->last_point().cast() - last_point; + Vec3d v = this->last_point().cast() - last_point; double lsqr = v.squaredNorm(); if (lsqr > distance * distance) { - Vec3d result = last_point + v * (distance / sqrt(lsqr)); - this->points.emplace_back(Point3(coord_t(result.x()), coord_t(result.y()), coord_t(result.z()))); + this->points.emplace_back((last_point + v * (distance / sqrt(lsqr))).cast()); + last_point_inserted = true; break; } distance -= sqrt(lsqr); } - // Clear fitting result if it's affected + // BBS: don't need to clip fitting result if it's empty + if (fitting_result.empty()) + return; + while (!fitting_result.empty() && fitting_result.back().start_point_index >= remove_after_index) + fitting_result.pop_back(); if (!fitting_result.empty()) { - while (!fitting_result.empty() && fitting_result.back().start_point_index >= remove_after_index) - fitting_result.pop_back(); - if (!fitting_result.empty()) { - fitting_result.back().end_point_index = this->points.size() - 1; + // BBS: last remaining segment is arc move, then clip the arc at last point + if (fitting_result.back().path_type == EMovePathType::Arc_move_ccw || + fitting_result.back().path_type == EMovePathType::Arc_move_cw) { + if (fitting_result.back().arc_data.clip_end(this->last_point().to_point())) + // BBS: succeed to clip arc, then update the last point + // TODO: fix z parameter + this->points.back() = Point3(fitting_result.back().arc_data.end_point, this->points.back().z()); + else + // BBS: Failed to clip arc, then back to linear move + fitting_result.back().path_type = EMovePathType::Linear_move; } + fitting_result.back().end_point_index = this->points.size() - 1; } } -void Polyline3::simplify(double tolerance) { +void Polyline3::simplify(double tolerance) +{ this->points = MultiPoint3::_douglas_peucker(this->points, tolerance); this->fitting_result.clear(); } -void Polyline3::simplify_by_fitting_arc(double tolerance) { - // For now, just use regular simplify - // Full ZAA implementation would use ArcFitter::do_arc_fitting_and_simplify - this->simplify(tolerance); +void Polyline3::simplify_by_fitting_arc(double tolerance) +{ + // BBS: do arc fit first, then use DP simplify to handle the straight part to reduce point. + Points points_2d = to_points(this->points); + ArcFitter::do_arc_fitting_and_simplify(points_2d, this->fitting_result, tolerance); + this->points = to_points3(points_2d); +} + +void Polyline3::reverse() +{ + // BBS: reverse points + MultiPoint3::reverse(); + // BBS: reverse the fitting_result + if (!this->fitting_result.empty()) { + for (size_t i = 0; i < this->fitting_result.size(); i++) { + std::swap(fitting_result[i].start_point_index, fitting_result[i].end_point_index); + fitting_result[i].start_point_index = MultiPoint3::size() - 1 - fitting_result[i].start_point_index; + fitting_result[i].end_point_index = MultiPoint3::size() - 1 - fitting_result[i].end_point_index; + if (fitting_result[i].is_arc_move()) + fitting_result[i].reverse_arc_path(); + } + std::reverse(this->fitting_result.begin(), this->fitting_result.end()); + } } bool Polyline3::split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) const @@ -737,7 +768,7 @@ bool Polyline3::split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) p2->append(this->last_point()); *p1 = *this; } else { - // Split first part + // BBS: spilit first part p1->clear(); p1->points.reserve(index + 1); p1->points.insert(p1->begin(), this->begin(), this->begin() + index + 1); @@ -745,7 +776,6 @@ bool Polyline3::split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) if (this->split_fitting_result_before_index(index, new_endpoint, p1->fitting_result)) p1->points.back() = new_endpoint; - // Split second part p2->clear(); p2->points.reserve(this->size() - index); p2->points.insert(p2->begin(), this->begin() + index, this->end()); @@ -756,58 +786,74 @@ bool Polyline3::split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) return true; } -void Polyline3::append(const Point3& point) { - // Don't append if same as last point +void Polyline3::append(const Point3& point) +{ + // // Don't append if same as last point + // if (!this->empty() && this->last_point() == point) + // return; + + // this->points.push_back(point); + // append_fitting_result_after_append_points(); + + // BBS: don't need to append same point if (!this->empty() && this->last_point() == point) return; - - this->points.push_back(point); - // Clear fitting result as structure changed - this->fitting_result.clear(); + MultiPoint3::append(point); + append_fitting_result_after_append_points(); } -void Polyline3::append(const Polyline3 &src) { +void Polyline3::append(const Polyline3& src) +{ if (!src.is_valid()) return; if (this->points.empty()) { this->points = src.points; this->fitting_result = src.fitting_result; } else { - // Append points - if (!src.points.empty() && !this->points.empty() && this->last_point() == src.points.front()) { - // Skip first point if it's the same as our last point - this->points.insert(this->points.end(), src.points.begin() + 1, src.points.end()); - } else { - this->points.insert(this->points.end(), src.points.begin(), src.points.end()); + // BBS: append the first point to create connection first, update the fitting date as well + this->append(src.points[0]); + // BBS: append a polyline which has fitting data to a polyline without fitting data. + // Then create a fake fitting data first, so that we can keep the fitting data in last polyline + if (this->fitting_result.empty() && !src.fitting_result.empty()) { + this->fitting_result.emplace_back(PathFittingData{0, this->points.size() - 1, EMovePathType::Linear_move, ArcSegment()}); } - // Note: Full arc fitting integration would merge fitting_result here - this->fitting_result.clear(); + // BBS: then append the remain points + MultiPoint3::append(src.points.begin() + 1, src.points.end()); + // BBS: finally append the fitting data + append_fitting_result_after_append_polyline(src); } } -void Polyline3::append_before(const Point3& point) { - // Don't append if same as first point +void Polyline3::append_before(const Point3& point) +{ + // BBS: don't need to append same point if (!this->empty() && this->first_point() == point) return; - - this->points.insert(this->points.begin(), point); - // Clear fitting result as structure changed - this->fitting_result.clear(); + if (this->size() == 1) { + this->fitting_result.clear(); + MultiPoint3::append(point); + MultiPoint3::reverse(); + } else { + this->reverse(); + this->append(point); + this->reverse(); + } } -void Polyline3::split_at(Point &point, Polyline3* p1, Polyline3* p2) const { +void Polyline3::split_at(Point& point, Polyline3* p1, Polyline3* p2) const +{ if (this->points.empty()) return; - // Check if the point is on the polyline + // 0 judge whether the point is on the polyline int index = this->find_point(point); if (index != -1) { - // The split point is on the polyline + // BBS: the spilit point is on the polyline, then easy split_at_index(index, p1, p2); point = p1->is_valid() ? p1->last_point().to_point() : p2->first_point().to_point(); return; } - // Find the line to split at + // 1 find the line to split at size_t line_idx = 0; Point p = this->first_point().to_point(); double min = (p - point).cast().norm(); @@ -815,13 +861,14 @@ void Polyline3::split_at(Point &point, Polyline3* p1, Polyline3* p2) const { for (Lines3::const_iterator line = lines.begin(); line != lines.end(); ++line) { Point p_tmp = point.projection_onto(line->to_line()); if ((p_tmp - point).cast().norm() < min) { - p = p_tmp; - min = (p - point).cast().norm(); + p = p_tmp; + min = (p - point).cast().norm(); line_idx = line - lines.begin(); } } - // Judge whether the closest point is one vertex of polyline + // 2 judge whether the cloest point is one vertex of polyline. + // and spilit the polyline at different index index = this->find_point(p); if (index != -1) { this->split_at_index(index, p1, p2); @@ -834,7 +881,6 @@ void Polyline3::split_at(Point &point, Polyline3* p1, Polyline3* p2) const { this->split_at_index(line_idx + 1, &temp, p2); p2->append_before(Point3(point, p2->first_point().z())); } - point = p; } void Polyline3::split_at(Point3 &point, Polyline3* p1, Polyline3* p2) const { @@ -845,46 +891,153 @@ void Polyline3::split_at(Point3 &point, Polyline3* p1, Polyline3* p2) const { bool Polyline3::split_at_length(const double length, Polyline3 *p1, Polyline3 *p2) const { if (this->points.empty()) return false; - if (length < 0 || length > this->length()) { return false; } + if (length < 0 || length > this->length()) { + return false; + } if (length < SCALED_EPSILON) { p1->clear(); - p1->append_before(this->first_point()); + p1->append(this->first_point()); *p2 = *this; } else if (is_approx(length, this->length(), SCALED_EPSILON)) { p2->clear(); - p2->append_before(this->last_point()); + p2->append(this->last_point()); *p1 = *this; } else { - // Find the line to split at + // 1 find the line to split at size_t line_idx = 0; double acc_length = 0; - Point p = this->first_point().to_point(); - for (const auto &l : this->lines()) { - p = l.b.to_point(); + Point3 p = this->first_point(); + for (const auto& l : this->lines()) { + p = l.b; const double current_length = l.length(); if (acc_length + current_length >= length) { - p = lerp(l.a.to_point(), l.b.to_point(), (length - acc_length) / current_length); + p = lerp(l.a, l.b, (length - acc_length) / current_length); break; } acc_length += current_length; line_idx++; } - // Judge whether the closest point is one vertex of polyline + // 2 judge whether the cloest point is one vertex of polyline. + // and spilit the polyline at different index int index = this->find_point(p); if (index != -1) { this->split_at_index(index, p1, p2); } else { Polyline3 temp; this->split_at_index(line_idx, p1, &temp); - p1->append_before(Point3(p, p1->last_point().z())); + p1->append(p); this->split_at_index(line_idx + 1, &temp, p2); - p2->append_before(Point3(p, p2->first_point().z())); + p2->append_before(p); } } return true; } +bool Polyline3::split_fitting_result_before_index(size_t index, Point3& new_endpoint, std::vector& data) const +{ + data.clear(); + new_endpoint = this->points[index]; + if (!this->fitting_result.empty()) { + // BBS: max size + data.reserve(this->fitting_result.size()); + // BBS: save fitting result before index + for (size_t i = 0; i < this->fitting_result.size(); i++) { + if (this->fitting_result[i].start_point_index < index) + data.push_back(this->fitting_result[i]); + else + break; + } + + if (!data.empty()) { + // BBS: need to clip the arc and generate new end point + if (data.back().is_arc_move() && data.back().end_point_index > index) { + if (!data.back().arc_data.clip_end(this->points[index].to_point())) + // BBS: failed to clip arc, then return to be linear move + data.back().path_type = EMovePathType::Linear_move; + else + // BBS: succeed to clip arc, then update and return the new end point + new_endpoint = Point3(data.back().arc_data.end_point, 0); + } + data.back().end_point_index = index; + } + data.shrink_to_fit(); + return true; + } + return false; +} + +bool Polyline3::split_fitting_result_after_index(size_t index, Point3& new_startpoint, std::vector& data) const +{ + data.clear(); + new_startpoint = this->points[index]; + if (!this->fitting_result.empty()) { + data.reserve(this->fitting_result.size()); + for (size_t i = 0; i < this->fitting_result.size(); i++) { + if (this->fitting_result[i].end_point_index > index) + data.push_back(this->fitting_result[i]); + } + if (!data.empty()) { + for (size_t i = 0; i < data.size(); i++) { + if (i != 0) { + data[i].start_point_index -= index; + data[i].end_point_index -= index; + } else { + data[i].end_point_index -= index; + // BBS: need to clip the arc and generate new start point + if (data.front().is_arc_move() && data.front().start_point_index < index) { + if (!data.front().arc_data.clip_start(this->points[index].to_point())) + // BBS: failed to clip arc, then return to be linear move + data.front().path_type = EMovePathType::Linear_move; + else + // BBS: succeed to clip arc, then update and return the new start point + new_startpoint = Point3(data.front().arc_data.start_point, 0); + } + data[i].start_point_index = 0; + } + } + } + data.shrink_to_fit(); + return true; + } + return false; +} + +void Polyline3::append_fitting_result_after_append_points() +{ + if (!fitting_result.empty()) { + if (fitting_result.back().is_linear_move()) { + fitting_result.back().end_point_index = this->points.size() - 1; + } else { + size_t new_start = fitting_result.back().end_point_index; + size_t new_end = this->points.size() - 1; + if (new_start != new_end) + fitting_result.emplace_back(PathFittingData{new_start, new_end, EMovePathType::Linear_move, ArcSegment()}); + } + } +} + +void Polyline3::append_fitting_result_after_append_polyline(const Polyline3& src) +{ + if (!this->fitting_result.empty()) { + // BBS: offset and save the fitting_result from src polyline + if (!src.fitting_result.empty()) { + size_t old_size = this->fitting_result.size(); + size_t index_offset = this->fitting_result.back().end_point_index; + this->fitting_result.insert(this->fitting_result.end(), src.fitting_result.begin(), src.fitting_result.end()); + for (size_t i = old_size; i < this->fitting_result.size(); i++) { + this->fitting_result[i].start_point_index += index_offset; + this->fitting_result[i].end_point_index += index_offset; + } + } else { + // BBS: the append polyline has no fitting data, then append as linear move directly + size_t new_start = this->fitting_result.back().end_point_index; + size_t new_end = this->size() - 1; + if (new_start != new_end) + this->fitting_result.emplace_back(PathFittingData{new_start, new_end, EMovePathType::Linear_move, ArcSegment()}); + } + } +} } diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index 6d409d3983..88671391db 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -315,7 +315,7 @@ public: void simplify_by_fitting_arc(double tolerance); // Reverse the polyline - using MultiPoint3::reverse; + void reverse(); // Split polyline at given index bool split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) const; @@ -344,15 +344,10 @@ public: std::vector fitting_result; private: - // Helper methods for split_at_index - bool split_fitting_result_before_index(size_t index, Point3 &new_endpoint, std::vector &result) const { - // Simplified stub - full implementation would handle arc fitting data - return false; - } - bool split_fitting_result_after_index(size_t index, Point3 &new_startpoint, std::vector &result) const { - // Simplified stub - full implementation would handle arc fitting data - return false; - } + void append_fitting_result_after_append_points(); + void append_fitting_result_after_append_polyline(const Polyline3& src); + bool split_fitting_result_before_index(size_t index, Point3& new_endpoint, std::vector& result) const; + bool split_fitting_result_after_index(size_t index, Point3& new_startpoint, std::vector& result) const; }; typedef std::vector Polylines3; From ba5b06196a83eed0afb1468ea9967bddf5272785 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Fri, 13 Mar 2026 02:12:10 -0700 Subject: [PATCH 14/28] Revert changes to 'ArcFitter' --- src/libslic3r/ArcFitter.cpp | 60 +++++++++---------------------------- src/libslic3r/ArcFitter.hpp | 2 -- 2 files changed, 14 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/ArcFitter.cpp b/src/libslic3r/ArcFitter.cpp index 1d48861e0c..cdfd708b10 100644 --- a/src/libslic3r/ArcFitter.cpp +++ b/src/libslic3r/ArcFitter.cpp @@ -1,5 +1,4 @@ #include "ArcFitter.hpp" -#include "Point.hpp" #include "Polyline.hpp" #include @@ -7,17 +6,7 @@ namespace Slic3r { -// Helper functions to dispatch to the correct douglas_peucker implementation -static inline Points douglas_peucker_helper(const Points &points, double tolerance) { - return MultiPoint::_douglas_peucker(points, tolerance); -} - -static inline Points3 douglas_peucker_helper(const Points3 &points, double tolerance) { - return MultiPoint3::_douglas_peucker(points, tolerance); -} - -template -static void do_arc_fitting_tmpl(const POINTS& points, std::vector& result, double tolerance) +void ArcFitter::do_arc_fitting(const Points& points, std::vector& result, double tolerance) { #ifdef DEBUG_ARC_FITTING static int irun = 0; @@ -50,7 +39,7 @@ static void do_arc_fitting_tmpl(const POINTS& points, std::vector 2) { //BBS: althought current point_stack can't be fit as arc, //but previous must can be fit if removing the top in stack, so save last arc - result.emplace_back(PathFittingData{ front_index, + result.emplace_back(std::move(PathFittingData{ front_index, back_index - 1, last_arc.direction == ArcDirection::Arc_Dir_CCW ? EMovePathType::Arc_move_ccw : EMovePathType::Arc_move_cw, - last_arc }); + last_arc })); } else { //BBS: save the first segment as line move when 3 point-line can't be fit as arc move if (result.empty() || result.back().path_type != EMovePathType::Linear_move) - result.emplace_back(PathFittingData{front_index, front_index + 1, EMovePathType::Linear_move, ArcSegment()}); + result.emplace_back(std::move(PathFittingData{front_index, front_index + 1, EMovePathType::Linear_move, ArcSegment()})); else if(result.back().path_type == EMovePathType::Linear_move) result.back().end_point_index = front_index + 1; } @@ -98,25 +87,14 @@ static void do_arc_fitting_tmpl(const POINTS& points, std::vector& result, double tolerance) -{ - do_arc_fitting_tmpl(points, result, tolerance); -} - -void ArcFitter::do_arc_fitting(const Points3 &points, std::vector& result, double tolerance) -{ - do_arc_fitting_tmpl(points, result, tolerance); -} - -template -static void do_arc_fitting_and_simplify_tmpl(POINTS &points, std::vector& result, double tolerance) +void ArcFitter::do_arc_fitting_and_simplify(Points& points, std::vector& result, double tolerance) { //BBS: 1 do arc fit first if (abs(tolerance) > SCALED_EPSILON) @@ -128,12 +106,12 @@ static void do_arc_fitting_and_simplify_tmpl(POINTS &points, std::vector reduce_count(result.size(), 0); @@ -146,11 +124,11 @@ static void do_arc_fitting_and_simplify_tmpl(POINTS &points, std::vector& result, double tolerance) -{ - do_arc_fitting_and_simplify_tmpl(points, result, tolerance); -} - -void ArcFitter::do_arc_fitting_and_simplify(Points3& points, std::vector& result, double tolerance) -{ - do_arc_fitting_and_simplify_tmpl(points, result, tolerance); -} - } \ No newline at end of file diff --git a/src/libslic3r/ArcFitter.hpp b/src/libslic3r/ArcFitter.hpp index 6672b0b771..f2b2ee49d8 100644 --- a/src/libslic3r/ArcFitter.hpp +++ b/src/libslic3r/ArcFitter.hpp @@ -42,11 +42,9 @@ class ArcFitter { public: //BBS: this function is used to check the point list and return which part can fit as arc, which part should be line static void do_arc_fitting(const Points& points, std::vector &result, double tolerance); - static void do_arc_fitting(const Points3& points, std::vector &result, double tolerance); //BBS: this function is used to check the point list and return which part can fit as arc, which part should be line. //By the way, it also use DP simplify to reduce point of straight part and only keep the start and end point of arc. static void do_arc_fitting_and_simplify(Points& points, std::vector& result, double tolerance); - static void do_arc_fitting_and_simplify(Points3& points, std::vector& result, double tolerance); }; } From 29e35d77b1264749710c655a2ad302ecc71a058a Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Mon, 16 Mar 2026 00:56:52 -0700 Subject: [PATCH 15/28] Fix mesh transformation logic PrintObject::contour_z to handle off center cases --- src/libslic3r/PrintObject.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d9f41a69a8..12d2d6855b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,4 +1,6 @@ #include "Exception.hpp" +#include "Model.hpp" +#include "Point.hpp" #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" @@ -728,9 +730,14 @@ void PrintObject::contour_z() throw RuntimeError("ContourZ: unexpected number of instances"); } - m_model_object->instances.front()->transform_mesh(&mesh, true); - sla::IndexedMesh imesh(mesh); + ModelInstance *inst = m_model_object->instances.front(); + Point center_offset = this->center_offset(); + Geometry::Transformation trans = inst->get_transformation(); + trans.set_offset(Vec3d(-unscale(center_offset.x()), -unscale(center_offset.y()), 0)); + mesh.transform(trans.get_matrix()); + + sla::IndexedMesh imesh(mesh); std::mutex mtx; size_t completed = 0; tbb::parallel_for( From 12232deb7a6eccc7639d711759b4edb11378ca54 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Mon, 16 Mar 2026 13:48:42 -0700 Subject: [PATCH 16/28] Fix z-offset handling for ZAA --- src/libslic3r/ContourZ.cpp | 9 +++------ src/libslic3r/PrintObject.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp index 0182b35aad..8eb1a8b5a8 100644 --- a/src/libslic3r/ContourZ.cpp +++ b/src/libslic3r/ContourZ.cpp @@ -30,8 +30,6 @@ static double slope_from_normal(const Eigen::Vector3d& normal) return angle_rad; } -// const int LINE = 180; - static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionPath &path) { if (region->region().config().zaa_region_disable) { @@ -213,14 +211,13 @@ static void contour_extrusion_entity(LayerRegion *region, const sla::IndexedMesh } static void handle_extrusion_collection(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntityCollection &collection, std::initializer_list roles) { - for (ExtrusionEntity *extr : collection.entities) { - // printf("handling extrusion collection %p %p\n", &collection, extr); - if (!contains(roles, extr->role())) { + for (ExtrusionEntity* extr : collection.entities) { + if (!contains(roles, extr->role())) { continue; } contour_extrusion_entity(region, mesh, extr); - } + } } void Layer::make_contour_z(const sla::IndexedMesh &mesh) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 12d2d6855b..5dd342eaf9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -731,13 +730,15 @@ void PrintObject::contour_z() } ModelInstance *inst = m_model_object->instances.front(); - Point center_offset = this->center_offset(); + Point center_offset = this->center_offset(); Geometry::Transformation trans = inst->get_transformation(); + double z = trans.get_offset().z() - unscale(this->height()) / 2; trans.set_offset(Vec3d(-unscale(center_offset.x()), -unscale(center_offset.y()), 0)); - mesh.transform(trans.get_matrix()); sla::IndexedMesh imesh(mesh); + imesh.ground_level_offset(-z); + std::mutex mtx; size_t completed = 0; tbb::parallel_for( From 1cc82873f483710b6c61e0397a76bd36426b577c Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Thu, 19 Mar 2026 01:32:51 -0700 Subject: [PATCH 17/28] Fix contour extrusion path logic and improve GCode extrusion handling --- src/libslic3r/ContourZ.cpp | 38 ++++++++++++++++++----------------- src/libslic3r/GCode.cpp | 6 ++---- src/libslic3r/PrintObject.cpp | 3 ++- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp index 8eb1a8b5a8..48a3da13c3 100644 --- a/src/libslic3r/ContourZ.cpp +++ b/src/libslic3r/ContourZ.cpp @@ -63,10 +63,14 @@ static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh & int num_segments = int(std::ceil(length_mm / resolution_mm)); Vec2d delta = line.vector(); - for (int i = 0; i < num_segments+1; i++) { - Vec2d p = p1d + delta*i/num_segments; + if (num_segments == 0) { + continue; + } - coordf_t x = p.x(); + for (int i = 0; i < num_segments + 1; i++) { + Vec2d p = p1d + delta * i / num_segments; + + coordf_t x = p.x(); coordf_t y = p.y(); sla::IndexedMesh::hit_result hit_up = mesh.query_ray_hit({x, y, mesh_z}, {0.0, 0.0, 1.0}); @@ -116,24 +120,22 @@ static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh & was_contoured = true; } - Vec3d new_point = {p.x(), p.y(), d}; + Vec3d new_point = {p.x(), p.y(), d}; - if (contoured_points.size() > 2) { - double dist = Linef3::distance_to_infinite_squared( - contoured_points[contoured_points.size() - 2], - contoured_points[contoured_points.size() - 1], - new_point); - if (dist < EPSILON) { - contoured_points[contoured_points.size() - 1] = new_point; - continue; - } - } + if (contoured_points.size() >= 2) { + double dist = Linef3::distance_to_infinite_squared(new_point, contoured_points[contoured_points.size() - 2], + contoured_points[contoured_points.size() - 1]); + if (dist < EPSILON * EPSILON) { + contoured_points[contoured_points.size() - 1] = new_point; + continue; + } + } - contoured_points.push_back(new_point); - } - } + contoured_points.push_back(new_point); + } + } - if (!was_contoured) { + if (!was_contoured) { return false; } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3693ffa872..39ac5b4d83 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6760,10 +6760,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (z < 0.1) { throw RuntimeError("GCode: very low z"); } - gcode += m_writer.extrude_to_xyz( - Vec3d(dest2d.x(), dest2d.y(), z), - e, - tempDescription + "; z_diff " + std::to_string(z_diff) + " " + ExtrusionEntity::role_to_string(path.role()) + "; eratio " + std::to_string(extrusion_ratio)); + gcode += m_writer.extrude_to_xyz(Vec3d(dest2d.x(), dest2d.y(), z), e, + GCodeWriter::full_gcode_comment ? tempDescription : ""); } else if (sloped == nullptr) { // Normal extrusion diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5dd342eaf9..e4d47c065e 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -732,7 +732,8 @@ void PrintObject::contour_z() ModelInstance *inst = m_model_object->instances.front(); Point center_offset = this->center_offset(); Geometry::Transformation trans = inst->get_transformation(); - double z = trans.get_offset().z() - unscale(this->height()) / 2; + + double z = this->m_model_object->min_z(); trans.set_offset(Vec3d(-unscale(center_offset.x()), -unscale(center_offset.y()), 0)); mesh.transform(trans.get_matrix()); From 95736445a479a3b508610daf02b66cc7d8103e9e Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Tue, 24 Mar 2026 09:18:38 -0700 Subject: [PATCH 18/28] Various ZAA fixes. - Handle z contouring in variable speed flow when emitting GCode - Add logic to restore nominnal z height for regular extrusions - preserve z_contoured flag when splitting extrusion paths --- src/libslic3r/AABBTreeLines.hpp | 37 ++-- src/libslic3r/ContourZ.cpp | 6 +- src/libslic3r/ExPolygon.hpp | 19 ++ src/libslic3r/ExtrusionEntity.cpp | 1 + src/libslic3r/GCode.cpp | 78 ++++++-- src/libslic3r/GCode.hpp | 2 + src/libslic3r/GCode/ExtrusionProcessor.hpp | 175 ++++++++---------- src/libslic3r/Line.hpp | 3 + src/libslic3r/Point.hpp | 2 +- .../Support/SupportSpotsGenerator.cpp | 4 +- 10 files changed, 190 insertions(+), 137 deletions(-) diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 55cf328f57..97ad1bdf44 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -161,10 +161,11 @@ namespace AABBTreeLines { // on centroids of the lines. // Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies // during tree traversal. - template - inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector& lines) + template + inline AABBTreeIndirect::Tree build_aabb_tree_over_indexed_lines( + const std::vector& lines) { - using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>; + using TreeType = AABBTreeIndirect::Tree; // using CoordType = typename TreeType::CoordType; using VectorType = typename TreeType::VectorType; using BoundingBox = typename TreeType::BoundingBox; @@ -303,7 +304,7 @@ namespace AABBTreeLines { private: std::vector lines; - AABBTreeIndirect::Tree<2, Scalar> tree; + AABBTreeIndirect::Tree tree; public: explicit LinesDistancer(const std::vector& lines) @@ -321,15 +322,15 @@ namespace AABBTreeLines { LinesDistancer() = default; // 1 true, -1 false, 0 cannot determine - int outside(const Vec<2, Scalar>& point) const { return point_outside_closed_contours(lines, tree, point); } + int outside(const Vec& point) const { return point_outside_closed_contours(lines, tree, point); } // negative sign means inside - template - std::tuple> distance_from_lines_extra(const Vec<2, Scalar>& point) const + template + std::tuple> distance_from_lines_extra(const Vec& point) const { size_t nearest_line_index_out = size_t(-1); - Vec<2, Floating> nearest_point_out = Vec<2, Floating>::Zero(); - Vec<2, Floating> p = point.template cast(); + Vec nearest_point_out = Vec::Zero(); + Vec p = point.template cast(); auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, nearest_line_index_out, nearest_point_out); if (distance < 0) { @@ -344,22 +345,20 @@ namespace AABBTreeLines { return { distance, nearest_line_index_out, nearest_point_out }; } - template - Floating distance_from_lines(const Vec<2, typename LineType::Scalar>& point) const + template Floating distance_from_lines(const Vec& point) const { auto [dist, idx, np] = distance_from_lines_extra(point); return dist; } - std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) - { - return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast(), radius * radius); - } - - template - std::vector, size_t>> intersections_with_line(const LineType& line) const + std::vector all_lines_in_radius(const Vec& point, Floating radius) { - return get_intersections_with_line>(lines, tree, line); + return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast(), radius * radius); + } + + template std::vector, size_t>> intersections_with_line(const LineType& line) const + { + return get_intersections_with_line>(lines, tree, line); } const LineType& get_line(size_t line_idx) const { return lines[line_idx]; } diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp index 48a3da13c3..49c28dca07 100644 --- a/src/libslic3r/ContourZ.cpp +++ b/src/libslic3r/ContourZ.cpp @@ -225,8 +225,8 @@ static void handle_extrusion_collection(LayerRegion *region, const sla::IndexedM void Layer::make_contour_z(const sla::IndexedMesh &mesh) { for (LayerRegion *region : this->regions()) { - handle_extrusion_collection(region, mesh, region->fills, {erTopSolidInfill, erIroning, erExternalPerimeter, erMixed}); - handle_extrusion_collection(region, mesh, region->perimeters, {erExternalPerimeter, erMixed}); - } + handle_extrusion_collection(region, mesh, region->fills, {erTopSolidInfill, erIroning, erPerimeter, erExternalPerimeter, erMixed}); + handle_extrusion_collection(region, mesh, region->perimeters, {erPerimeter, erExternalPerimeter, erMixed}); + } } } // namespace Slic3r diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 87a1146d41..ce7ebe892f 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -192,6 +192,25 @@ inline Linesf to_unscaled_linesf(const ExPolygons &src) return lines; } +inline Linesf3 to_unscaled_linesf3(const ExPolygons& src) +{ + Linesf3 lines; + lines.reserve(count_points(src)); + for (ExPolygons::const_iterator it_expoly = src.begin(); it_expoly != src.end(); ++it_expoly) { + for (size_t i = 0; i <= it_expoly->holes.size(); ++i) { + const Points& points = ((i == 0) ? it_expoly->contour : it_expoly->holes[i - 1]).points; + Vec2d unscaled_a = unscaled(points.front()); + Vec2d unscaled_b = unscaled_a; + for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) { + unscaled_b = unscaled(*(it)); + lines.push_back(Linef3(unscaled_a, unscaled_b, 0)); + unscaled_a = unscaled_b; + } + lines.push_back(Linef3(unscaled_a, unscaled(points.front()), 0)); + } + } + return lines; +} inline Points to_points(const ExPolygons &src) { diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 68df506d31..36955a19bd 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -286,6 +286,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const const ExtrusionPath &path = this->paths[path_idx]; ExtrusionPath p1(path.role(), path.mm3_per_mm, path.width, path.height); ExtrusionPath p2(path.role(), path.mm3_per_mm, path.width, path.height); + p1.z_contoured = p2.z_contoured = path.z_contoured; path.polyline.split_at(p, &p1.polyline, &p2.polyline); if (this->paths.size() == 1) { diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 39ac5b4d83..831d30ac42 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1,5 +1,6 @@ #include "BoundingBox.hpp" #include "Config.hpp" +#include "GCodeWriter.hpp" #include "Polygon.hpp" #include "PrintConfig.hpp" #include "libslic3r.h" @@ -5817,7 +5818,7 @@ std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref, const Point3 ¢er3 = paths.front().polyline.points.front(); pt.rotate(angle, Point(center3.x(), center3.y())); // generate the travel move - gcode += m_writer.extrude_to_xy(this->point_to_gcode(pt), 0,"move inwards before travel",true); + gcode += m_writer.extrude_to_xy(this->point_to_gcode(pt), 0, "move inwards before travel", true); } return gcode; @@ -6150,19 +6151,13 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // Move to first point of extrusion path // path is 2D. But in slope lift case, lift z is done in travel_to function. // Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance - Point3 first_point = path.first_point3(); - if (!m_last_pos_defined || m_last_pos != first_point || m_need_change_layer_lift_z || slope_need_z_travel) { + Point first_point = path.first_point(); + if (!m_last_pos_defined || m_last_pos.to_point() != first_point || m_need_change_layer_lift_z || slope_need_z_travel) { const bool _last_pos_undefined = !m_last_pos_defined; - double z = DBL_MAX; - if (sloped != nullptr) { - z = get_sloped_z(sloped->slope_begin.z_ratio); - } else if ((!m_last_pos_defined && first_point.z() != 0) || m_last_pos.z() != first_point.z()) { - z = m_nominal_z + unscale_(first_point.z()); - if (z < 0.1) { - throw RuntimeError("GCode: very low z"); - } - } - gcode += this->travel_to(path.first_point(), path.role(), "move to first " + description + " point", z); + + gcode += this->travel_to(path.first_point(), path.role(), "move to first " + description + " point", + sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio)); + m_need_change_layer_lift_z = false; // Orca: ensure Z matches planned layer height if (_last_pos_undefined && !slope_need_z_travel) { @@ -6170,6 +6165,20 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } } + if (path.z_contoured && !path.polyline.lines().empty()) { + double current_z = m_writer.get_position().z(); + ; + double first_z = unscale_(path.polyline.lines().begin()->a.z()) + m_nominal_z; + if (GCodeFormatter::quantize_xyzf(first_z) != GCodeFormatter::quantize_xyzf(current_z)) { + gcode += m_writer.travel_to_z(first_z, "set Z for contouring", true); + } + } + if (!path.z_contoured) { + double current_z = m_writer.get_position().z(); + if (GCodeFormatter::quantize_xyzf(current_z) != GCodeFormatter::quantize_xyzf(m_nominal_z)) { + gcode += this->writer().travel_to_z(m_nominal_z, "reset Z after contouring", true); + } + } // if needed, write the gcode_label_objects_end then gcode_label_objects_start // should be already done by travel_to, but just in case @@ -6729,6 +6738,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr || path.z_contoured) { double path_length = 0.; double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR; + double saved_z = m_writer.get_position().z(); + for (const Line3& line : path.polyline.lines()) { std::string tempDescription = description; const double line_length = line.length() * SCALING_FACTOR; @@ -6849,14 +6860,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, double total_length = 0; if (sloped != nullptr) { // Calculate total extrusion length - Points p; + Points3 p; p.reserve(new_points.size()); std::transform(new_points.begin(), new_points.end(), std::back_inserter(p), [](const ProcessedPoint& pp) { return pp.p; }); - Polyline l(p); + Polyline3 l(p); total_length = l.length() * SCALING_FACTOR; } gcode += m_writer.set_speed(last_set_speed, "", comment); - Vec2d prev = this->point_to_gcode_quantized(new_points[0].p); + Vec3d prev = this->point_to_gcode_quantized(new_points[0].p); bool pre_fan_enabled = false; bool cur_fan_enabled = false; if( m_enable_cooling_markers && enable_overhang_bridge_fan) @@ -6870,7 +6881,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, std::string tempDescription = description; const ProcessedPoint &processed_point = new_points[i]; const ProcessedPoint &pre_processed_point = new_points[i-1]; - Vec2d p = this->point_to_gcode_quantized(processed_point.p); + Vec3d p = this->point_to_gcode_quantized(processed_point.p); if (m_enable_cooling_markers) { if (enable_overhang_bridge_fan) { cur_fan_enabled = check_overhang_fan(processed_point.overlap, path.role()); @@ -6952,9 +6963,26 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length); } } - if (sloped == nullptr) { + if (path.z_contoured) { + Vec2d dest2d = p.head<2>(); + coordf_t z_diff = unscale_(processed_point.p.z()); + + double extrusion_ratio = 1; + if (path.role() != erIroning) { + extrusion_ratio = (path.height + z_diff) / path.height; + } + + double e = dE * extrusion_ratio; + + double z = m_nominal_z + z_diff; + if (z < 0.1) { + throw RuntimeError("GCode: very low z"); + } + gcode += m_writer.extrude_to_xyz(Vec3d(dest2d.x(), dest2d.y(), z), e, + GCodeWriter::full_gcode_comment ? tempDescription : ""); + } else if (sloped == nullptr) { // Normal extrusion - gcode += m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : ""); + gcode += m_writer.extrude_to_xy(p.head<2>(), dE, GCodeWriter::full_gcode_comment ? tempDescription : ""); } else { // Sloped extrusion const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length); @@ -7811,6 +7839,13 @@ Vec2d GCode::point_to_gcode(const Point &point) const return unscale(point) + m_origin - extruder_offset; } +Vec3d GCode::point_to_gcode(const Point3& point) const +{ + Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); + Vec2d xy = unscale(point.to_point()) + m_origin - extruder_offset; + return Vec3d(xy.x(), xy.y(), unscale_(point.z())); +} + // convert a model-space scaled point into G-code coordinates Point GCode::gcode_to_point(const Vec2d &point) const { @@ -7828,6 +7863,11 @@ Vec2d GCode::point_to_gcode_quantized(const Point& point) const return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) }; } +Vec3d GCode::point_to_gcode_quantized(const Point3& point) const +{ + Vec3d p = this->point_to_gcode(point); + return {GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()), GCodeFormatter::quantize_xyzf(p.z())}; +} // Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed // during infill/perimeter wiping, or normally (depends on wiping_entities parameter) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index cdc0698642..2133d97b9b 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -223,8 +223,10 @@ public: void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); } Point last_pos() const { return m_last_pos.to_point(); } Vec2d point_to_gcode(const Point &point) const; + Vec3d point_to_gcode(const Point3& point) const; Point gcode_to_point(const Vec2d &point) const; Vec2d point_to_gcode_quantized(const Point& point) const; + Vec3d point_to_gcode_quantized(const Point3& point) const; const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } GCodeWriter& writer() { return m_writer; } diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index dad88a7485..b282af8f4e 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -27,19 +27,19 @@ namespace Slic3r { -struct ExtendedPoint +template struct ExtendedPoint { - Vec2d position; + Eigen::Matrix position; float distance; float curvature; }; template -std::vector estimate_points_properties(const POINTS &input_points, - const AABBTreeLines::LinesDistancer &unscaled_prev_layer, - float flow_width, - float max_line_length = -1.0f, - float min_distance = -1.0f) +std::vector> estimate_points_properties(const POINTS& input_points, + const AABBTreeLines::LinesDistancer& unscaled_prev_layer, + float flow_width, + float max_line_length = -1.0f, + float min_distance = -1.0f) { bool looped = input_points.front() == input_points.back(); std::function get_prev_index = [](size_t idx, size_t count) { @@ -78,47 +78,36 @@ std::vector estimate_points_properties(const POINTS double min_spacing = flow_width*0.25; using AABBScalar = typename AABBTreeLines::LinesDistancer::Scalar; + using Vec = Eigen::Matrix; if (input_points.empty()) return {}; float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; - auto maybe_unscale = [](const P &p) -> Vec2d { - if constexpr (P::RowsAtCompileTime == 3) { - // 3D point - extract XY only - if constexpr (SCALED_INPUT) { - return unscaled(p).template head<2>(); - } else { - return p.template head<2>().template cast(); - } - } else { - // 2D point - use as is - return SCALED_INPUT ? unscaled(p) : p.template cast(); - } - }; + auto maybe_unscale = [](const P& p) -> Vec { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; - std::vector points; + std::vector> points; points.reserve(input_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1)); { - ExtendedPoint start_point{maybe_unscale(input_points.front())}; - auto [distance, nearest_line, - x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); + ExtendedPoint start_point{maybe_unscale(input_points.front())}; + auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra( + start_point.position.template cast()); start_point.distance = distance + boundary_offset; points.push_back(start_point); } for (size_t i = 1; i < input_points.size(); i++) { - ExtendedPoint next_point{maybe_unscale(input_points[i])}; + ExtendedPoint next_point{maybe_unscale(input_points[i])}; auto [distance, nearest_line, - x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); + x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.template cast()); next_point.distance = distance + boundary_offset; // Intersection handling if (ADD_INTERSECTIONS && ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) { - const ExtendedPoint &prev_point = points.back(); - auto intersections = unscaled_prev_layer.template intersections_with_line( - L{prev_point.position.cast(), next_point.position.cast()}); + const ExtendedPoint& prev_point = points.back(); + auto intersections = unscaled_prev_layer.template intersections_with_line( + L{prev_point.position.template cast(), next_point.position.template cast()}); for (const auto &intersection : intersections) { - ExtendedPoint p{}; + ExtendedPoint p{}; p.position = intersection.first.template cast(); p.distance = boundary_offset; // ORCA: Filter out points that are introduced at intersections if their distance from the previous or next point is not meaningful @@ -133,12 +122,12 @@ std::vector estimate_points_properties(const POINTS // Segmentation handling if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) { - std::vector new_points; + std::vector> new_points; new_points.reserve(points.size() * 2); new_points.push_back(points.front()); for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) { - const ExtendedPoint &curr = points[point_idx]; - const ExtendedPoint &next = points[point_idx + 1]; + const ExtendedPoint& curr = points[point_idx]; + const ExtendedPoint& next = points[point_idx + 1]; if ((curr.distance > -boundary_offset && curr.distance < boundary_offset + 2.0f) || (next.distance > -boundary_offset && next.distance < boundary_offset + 2.0f)) { @@ -156,10 +145,10 @@ std::vector estimate_points_properties(const POINTS double t1 = std::max(a0, a1); if (t0 < 1.0) { - Vec2d p0 = curr.position + t0 * (next.position - curr.position); - auto [p0_dist, p0_near_l, - p0_x] = unscaled_prev_layer.template distance_from_lines_extra(p0.cast()); - ExtendedPoint new_p{}; + Vec p0 = curr.position + t0 * (next.position - curr.position); + auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra( + p0.template cast()); + ExtendedPoint new_p{}; new_p.position = p0; new_p.distance = float(p0_dist + boundary_offset); // ORCA: only create a new point in the path if the new point overhang distance will be used to generate a speed change @@ -173,10 +162,10 @@ std::vector estimate_points_properties(const POINTS } } if (t1 > 0.0) { - Vec2d p1 = curr.position + t1 * (next.position - curr.position); - auto [p1_dist, p1_near_l, - p1_x] = unscaled_prev_layer.template distance_from_lines_extra(p1.cast()); - ExtendedPoint new_p{}; + Vec p1 = curr.position + t1 * (next.position - curr.position); + auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra( + p1.template cast()); + ExtendedPoint new_p{}; new_p.position = p1; new_p.distance = float(p1_dist + boundary_offset); // ORCA: only create a new point in the path if the new point overhang distance will be used to generate a speed change @@ -198,21 +187,21 @@ std::vector estimate_points_properties(const POINTS // Maximum line length handling if (max_line_length > 0) { - std::vector new_points; + std::vector> new_points; new_points.reserve(points.size() * 2); { for (size_t i = 0; i + 1 < points.size(); i++) { - const ExtendedPoint &curr = points[i]; - const ExtendedPoint &next = points[i + 1]; + const ExtendedPoint& curr = points[i]; + const ExtendedPoint& next = points[i + 1]; new_points.push_back(curr); double len = (next.position - curr.position).squaredNorm(); double t = sqrt((max_line_length * max_line_length) / len); size_t new_point_count = 1.0 / t; for (size_t j = 1; j < new_point_count + 1; j++) { - Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t); + Vec pos = curr.position * (1.0 - j * t) + next.position * (j * t); auto [p_dist, p_near_l, - p_x] = unscaled_prev_layer.template distance_from_lines_extra(pos.cast()); - ExtendedPoint new_p{}; + p_x] = unscaled_prev_layer.template distance_from_lines_extra(pos.template cast()); + ExtendedPoint new_p{}; new_p.position = pos; new_p.distance = float(p_dist + boundary_offset); @@ -231,8 +220,8 @@ std::vector estimate_points_properties(const POINTS float accumulated_distance = 0; std::vector distances_for_curvature(points.size()); for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) { - const ExtendedPoint &a = points[point_idx]; - const ExtendedPoint &b = points[get_prev_index(point_idx, points.size())]; + const ExtendedPoint& a = points[point_idx]; + const ExtendedPoint& b = points[get_prev_index(point_idx, points.size())]; distances_for_curvature[point_idx] = (b.position - a.position).norm(); accumulated_distance += distances_for_curvature[point_idx]; @@ -241,9 +230,9 @@ std::vector estimate_points_properties(const POINTS if (accumulated_distance > EPSILON) for (float window_size : {3.0f, 9.0f, 16.0f}) { for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { - ExtendedPoint ¤t = points[point_idx]; + ExtendedPoint& current = points[point_idx]; - Vec2d back_position = current.position; + Vec back_position = current.position; { size_t back_point_index = point_idx; float dist_backwards = 0; @@ -263,7 +252,7 @@ std::vector estimate_points_properties(const POINTS } } - Vec2d front_position = current.position; + Vec front_position = current.position; { size_t front_point_index = point_idx; float dist_forwards = 0; @@ -283,7 +272,9 @@ std::vector estimate_points_properties(const POINTS } } - float new_curvature = angle(current.position - back_position, front_position - current.position) / window_size; + float new_curvature = angle((current.position - back_position).template head<2>(), + (front_position - current.position).template head<2>()) / + window_size; if (abs(current.curvature) < abs(new_curvature)) { current.curvature = new_curvature; } @@ -295,15 +286,15 @@ std::vector estimate_points_properties(const POINTS struct ProcessedPoint { - Point p; + Point3 p; float speed = 1.0f; float overlap = 1.0f; }; class ExtrusionQualityEstimator { - std::unordered_map> prev_layer_boundaries; - std::unordered_map> next_layer_boundaries; + std::unordered_map> prev_layer_boundaries; + std::unordered_map> next_layer_boundaries; std::unordered_map> prev_curled_extrusions; std::unordered_map> next_curled_extrusions; const PrintObject *current_object; @@ -316,7 +307,7 @@ public: if (layer == nullptr) return; const PrintObject *object = obj; prev_layer_boundaries[object] = next_layer_boundaries[object]; - next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)}; + next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf3(layer->lslices)}; prev_curled_extrusions[object] = next_curled_extrusions[object]; next_curled_extrusions[object] = AABBTreeLines::LinesDistancer{layer->curled_lines}; } @@ -371,56 +362,54 @@ public: smallest_distance_with_lower_speed=-1.f; // Orca: Pass to the point properties estimator the smallest ovehang distance that triggers a slowdown (smallest_distance_with_lower_speed) - std::vector extended_points = estimate_points_properties - (path.polyline.points, - prev_layer_boundaries[current_object], - path.width, - -1, - smallest_distance_with_lower_speed); + std::vector> extended_points = + estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width, -1, + smallest_distance_with_lower_speed); const auto width_inv = 1.0f / path.width; std::vector processed_points; processed_points.reserve(extended_points.size()); for (size_t i = 0; i < extended_points.size(); i++) { - const ExtendedPoint &curr = extended_points[i]; - const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; - + const ExtendedPoint<3>& curr = extended_points[i]; + const ExtendedPoint<3>& next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; + float artificial_distance_to_curled_lines = 0.0; if(slowdown_for_curled_edges) { // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines const double dist_limit = 10.0 * path.width; { - Vec2d middle = 0.5 * (curr.position + next.position); - auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); - if (!line_indices.empty()) { - double len = (next.position - curr.position).norm(); - // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle of this long line + Vec3d middle = 0.5 * (curr.position + next.position); + auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), + scale_(dist_limit)); + if (!line_indices.empty()) { + double len = (next.position - curr.position).norm(); + // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle of this long line // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge if (len > 2) { - Vec2d dir = Vec2d(next.position - curr.position) / len; - Vec2d right = Vec2d(-dir.y(), dir.x()); + Vec2d dir = Vec2d(next.position.head<2>() - curr.position.head<2>()) / len; + Vec2d right = Vec2d(-dir.y(), dir.x()); - Polygon box_of_influence = { - scaled(Vec2d(curr.position + right * dist_limit)), - scaled(Vec2d(next.position + right * dist_limit)), - scaled(Vec2d(next.position - right * dist_limit)), - scaled(Vec2d(curr.position - right * dist_limit)), - }; + Polygon box_of_influence = { + scaled(Vec2d(curr.position.head<2>() + right * dist_limit)), + scaled(Vec2d(next.position.head<2>() + right * dist_limit)), + scaled(Vec2d(next.position.head<2>() - right * dist_limit)), + scaled(Vec2d(curr.position.head<2>() - right * dist_limit)), + }; - double projected_lengths_sum = 0; - for (size_t idx : line_indices) { - const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx); - Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); + double projected_lengths_sum = 0; + for (size_t idx : line_indices) { + const CurledLine& line = prev_curled_extrusions[current_object].get_line(idx); + Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); if (inside.empty()) continue; double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); projected_lengths_sum += projected_length; - } - if (projected_lengths_sum < 0.4 * len) { - line_indices.clear(); - } - } + } + if (projected_lengths_sum < 0.4 * len) { + line_indices.clear(); + } + } for (size_t idx : line_indices) { const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx); @@ -430,9 +419,9 @@ public: (line.curled_height / (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist); } - } - } - } + } + } + } auto calculate_speed = [&speed_sections, &original_speed](float distance) { float final_speed; @@ -464,8 +453,8 @@ public: } float overlap = std::min(1 - (curr.distance+artificial_distance_to_curled_lines) * width_inv, 1 - (next.distance+artificial_distance_to_curled_lines) * width_inv); - - processed_points.push_back({ scaled(curr.position), extrusion_speed, overlap }); + + processed_points.push_back({Point3(scaled(curr.position)), extrusion_speed, overlap}); } return processed_points; } diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 99935b1fc4..d8240702b2 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -271,6 +271,7 @@ class Linef3 public: Linef3() : a(Vec3d::Zero()), b(Vec3d::Zero()) {} Linef3(const Vec3d& _a, const Vec3d& _b) : a(_a), b(_b) {} + Linef3(const Vec2d& _a, const Vec2d& _b, double z) : a(Vec3d(_a.x(), _a.y(), z)), b(Vec3d(_b.x(), _b.y(), z)) {} Vec3d intersect_plane(double z) const; void scale(double factor) { this->a *= factor; this->b *= factor; } @@ -311,6 +312,8 @@ public: using Scalar = Vec3d::Scalar; }; +using Linesf3 = std::vector; + BoundingBox get_extents(const Lines &lines); } // namespace Slic3r diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 83945d73ce..039f361eaa 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -279,7 +279,7 @@ public: Point3(int64_t x, int64_t y, int64_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {} Point3(double x, double y, double z = 0.0) : Vec3crd(coord_t(std::round(x)), coord_t(std::round(y)), coord_t(std::round(z))) {} Point3(const Point3 &rhs) { *this = rhs; } - explicit Point3(const Point &rhs, coord_t z = 0) : Vec3crd(rhs.x(), rhs.y(), z) {} + explicit Point3(const Vec2crd& vec2crd, coord_t z = 0) : Vec3crd(vec2crd.x(), vec2crd.y(), z) {} explicit Point3(const Vec3crd &vec3crd) : Vec3crd(vec3crd) {} // This constructor allows you to construct Point from Eigen expressions template diff --git a/src/libslic3r/Support/SupportSpotsGenerator.cpp b/src/libslic3r/Support/SupportSpotsGenerator.cpp index 057a64b0f8..6fb4908e67 100644 --- a/src/libslic3r/Support/SupportSpotsGenerator.cpp +++ b/src/libslic3r/Support/SupportSpotsGenerator.cpp @@ -165,8 +165,8 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) flow_width, params.bridge_distance); for (size_t i = 0; i < annotated_points.size(); ++i) { - const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - const ExtendedPoint &b = annotated_points[i]; + const ExtendedPoint<2>& a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + const ExtendedPoint<2>& b = annotated_points[i]; ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), extrusion}; From a5e12529f86305b975b061fa550fa0aede3e5303 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Thu, 26 Mar 2026 03:12:47 -0700 Subject: [PATCH 19/28] Treat internal and external perimeters the same for the purpose of ZAA --- src/libslic3r/ContourZ.cpp | 40 ++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp index 49c28dca07..27efe25f08 100644 --- a/src/libslic3r/ContourZ.cpp +++ b/src/libslic3r/ContourZ.cpp @@ -89,21 +89,23 @@ static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh & min_down = -(height + 0.1); } - double slope_rad = slope_from_normal(normal); - double slope_degrees = slope_rad * 180.0 / M_PI; + if (is_perimeter(path.role())) { + double slope_rad = slope_from_normal(normal); + double slope_degrees = slope_rad * 180.0 / M_PI; - if (d > min_down && minimize_perimeter_height_angle > 0 && minimize_perimeter_height_angle < slope_degrees && path.role() == erExternalPerimeter) { - double adjustment = follow_slope_down(slope_rad, half_width); - if (adjustment > 0) { - throw RuntimeError("ContourZ: got positive adjustment"); - } - d += adjustment; - if (d < min_down) { - d = min_down; - } - } + if (d > min_down && minimize_perimeter_height_angle > 0 && minimize_perimeter_height_angle < slope_degrees) { + double adjustment = follow_slope_down(slope_rad, half_width); + if (adjustment > 0) { + throw RuntimeError("ContourZ: got positive adjustment"); + } + d += adjustment; + if (d < min_down) { + d = min_down; + } + } + } - if (d > max_up + 0.03 || d < min_down) { + if (d > max_up + 0.03 || d < min_down) { d = 0; } else { if (d > max_up) { @@ -111,12 +113,12 @@ static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh & } } - if (path.role() == erExternalPerimeter && d > 0) { - // do not increase height of external perimeters as this may create an appearance of a seam - d = 0; - } - - if (std::abs(d) > EPSILON) { + if (is_perimeter(path.role()) && d > 0) { + // do not increase height of perimeters as this may create an appearance of a seam + d = 0; + } + + if (std::abs(d) > EPSILON) { was_contoured = true; } From 39ef7a5b7aed2104c4a7f0ff25d1c03eb3f01a2f Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Thu, 26 Mar 2026 04:21:36 -0700 Subject: [PATCH 20/28] Adjust ZAA configuration --- src/libslic3r/ContourZ.cpp | 9 +- src/libslic3r/Preset.cpp | 394 ++++++++++++++++++++++++++----- src/libslic3r/Print.cpp | 2 +- src/libslic3r/Print.hpp | 1 + src/libslic3r/PrintConfig.cpp | 12 +- src/libslic3r/PrintConfig.hpp | 214 ++++++----------- src/libslic3r/PrintObject.cpp | 16 ++ src/slic3r/GUI/GUI_Factories.cpp | 75 ++++-- src/slic3r/GUI/Tab.cpp | 2 - 9 files changed, 482 insertions(+), 243 deletions(-) diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp index 27efe25f08..49c6cd5d68 100644 --- a/src/libslic3r/ContourZ.cpp +++ b/src/libslic3r/ContourZ.cpp @@ -32,11 +32,7 @@ static double slope_from_normal(const Eigen::Vector3d& normal) static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionPath &path) { - if (region->region().config().zaa_region_disable) { - return false; - } - - if (path.role() != erTopSolidInfill && path.role() != erIroning && path.role() != erExternalPerimeter && path.role() != erPerimeter) { + if (path.role() != erTopSolidInfill && path.role() != erIroning && path.role() != erExternalPerimeter && path.role() != erPerimeter) { return false; } @@ -227,6 +223,9 @@ static void handle_extrusion_collection(LayerRegion *region, const sla::IndexedM void Layer::make_contour_z(const sla::IndexedMesh &mesh) { for (LayerRegion *region : this->regions()) { + if (!region->region().config().zaa_enabled) + continue; + handle_extrusion_collection(region, mesh, region->fills, {erTopSolidInfill, erIroning, erPerimeter, erExternalPerimeter, erMixed}); handle_extrusion_collection(region, mesh, region->perimeters, {erPerimeter, erExternalPerimeter, erMixed}); } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index a90f7953a2..d448e0839f 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -887,76 +887,344 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle) return false; } -static std::vector s_Preset_print_options { - "layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "spiral_starting_flow_ratio", "spiral_finishing_flow_ratio", "slicing_mode", - "top_shell_layers", "top_shell_thickness", "top_surface_density", "bottom_surface_density", "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","fill_multiline", "sparse_infill_pattern", "lateral_lattice_angle_1", "lateral_lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern", - "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", - "align_infill_direction_to_model", "extra_solid_infills", - "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_angle_fixed", "ironing_inset", - "support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing", +static std::vector s_Preset_print_options{ + "layer_height", + "initial_layer_print_height", + "wall_loops", + "alternate_extra_wall", + "slice_closing_radius", + "spiral_mode", + "spiral_mode_smooth", + "spiral_mode_max_xy_smoothing", + "spiral_starting_flow_ratio", + "spiral_finishing_flow_ratio", + "slicing_mode", + "top_shell_layers", + "top_shell_thickness", + "top_surface_density", + "bottom_surface_density", + "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", + "fill_multiline", + "sparse_infill_pattern", + "lateral_lattice_angle_1", + "lateral_lattice_angle_2", + "infill_overhang_angle", + "top_surface_pattern", + "bottom_surface_pattern", + "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", + "align_infill_direction_to_model", + "extra_solid_infills", + "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_angle_fixed", + "ironing_inset", + "support_ironing", + "support_ironing_pattern", + "support_ironing_flow", + "support_ironing_spacing", "max_travel_detour_distance", - "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_mode", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", - "max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length","extrusion_rate_smoothing_external_perimeter_only", - "inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed", - "top_surface_speed", "support_speed", "support_object_xy_distance", "support_object_first_layer_gap", "support_interface_speed", - "bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed", - "outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height","single_loop_draft_shield", "draft_shield", - "brim_width", "brim_object_gap", "brim_use_efc_outline", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", - "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", - "support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style", + "fuzzy_skin", + "fuzzy_skin_thickness", + "fuzzy_skin_point_distance", + "fuzzy_skin_first_layer", + "fuzzy_skin_noise_type", + "fuzzy_skin_mode", + "fuzzy_skin_scale", + "fuzzy_skin_octaves", + "fuzzy_skin_persistence", + "max_volumetric_extrusion_rate_slope", + "max_volumetric_extrusion_rate_slope_segment_length", + "extrusion_rate_smoothing_external_perimeter_only", + "inner_wall_speed", + "outer_wall_speed", + "sparse_infill_speed", + "internal_solid_infill_speed", + "top_surface_speed", + "support_speed", + "support_object_xy_distance", + "support_object_first_layer_gap", + "support_interface_speed", + "bridge_speed", + "internal_bridge_speed", + "gap_infill_speed", + "travel_speed", + "travel_speed_z", + "initial_layer_speed", + "outer_wall_acceleration", + "initial_layer_acceleration", + "top_surface_acceleration", + "default_acceleration", + "skirt_type", + "skirt_loops", + "skirt_speed", + "min_skirt_length", + "skirt_distance", + "skirt_start_angle", + "skirt_height", + "single_loop_draft_shield", + "draft_shield", + "brim_width", + "brim_object_gap", + "brim_use_efc_outline", + "brim_type", + "brim_ears_max_angle", + "brim_ears_detection_length", + "enable_support", + "support_type", + "support_threshold_angle", + "support_threshold_overlap", + "enforce_support_layers", + "raft_layers", + "raft_first_layer_density", + "raft_first_layer_expansion", + "raft_contact_distance", + "raft_expansion", + "support_base_pattern", + "support_base_pattern_spacing", + "support_expansion", + "support_style", // BBS - "print_extruder_id", "print_extruder_variant", + "print_extruder_id", + "print_extruder_variant", "independent_support_layer_height", - "support_angle", "support_interface_top_layers", "support_interface_bottom_layers", - "support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern", - "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", - "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_enable_framework", - "prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_volume", + "support_angle", + "support_interface_top_layers", + "support_interface_bottom_layers", + "support_interface_pattern", + "support_interface_spacing", + "support_interface_loop_pattern", + "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", + "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_enable_framework", + "prime_tower_width", + "prime_tower_brim_width", + "prime_tower_skip_points", + "prime_volume", "prime_tower_infill_gap", "prime_tower_flat_ironing", "enable_tower_interface_features", "enable_tower_interface_cooldown_during_tower", - "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", - "flush_into_infill", "flush_into_objects", "flush_into_support", - "tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter", - "tree_support_branch_diameter", "tree_support_branch_diameter_angle", - "detect_narrow_internal_solid_infill", - "gcode_add_line_number", "enable_arc_fitting", "precise_z_height", "infill_combination","infill_combination_max_layer_height", /*"adaptive_layer_height",*/ - "support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed", - "initial_layer_infill_speed", "only_one_wall_top", - "timelapse_type", - "wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "wall_distribution_count", "min_feature_size", "min_bead_width", "post_process", "min_length_factor", - "small_perimeter_speed", "small_perimeter_threshold","bridge_angle","internal_bridge_angle", "filter_out_gap_fill", "travel_acceleration","inner_wall_acceleration", "min_width_top_surface", - "default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk","default_junction_deviation", - "top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer", "print_flow_ratio", "seam_gap", - "set_other_flow_ratios", "first_layer_flow_ratio", "outer_wall_flow_ratio", "inner_wall_flow_ratio", "overhang_flow_ratio", "sparse_infill_flow_ratio", "internal_solid_infill_flow_ratio", "gap_fill_flow_ratio", "support_flow_ratio", "support_interface_flow_ratio", - "role_based_wipe_speed", "wipe_speed", "accel_to_decel_enable", "accel_to_decel_factor", "wipe_on_loops", "wipe_before_external_loop", - "bridge_density","internal_bridge_density", "precise_outer_wall", "bridge_acceleration", - "sparse_infill_acceleration", "internal_solid_infill_acceleration", "tree_support_auto_brim", - "tree_support_brim_width", "gcode_comments", "gcode_label_objects", - "initial_layer_travel_speed", "exclude_object", "slow_down_layers", "infill_anchor", "infill_anchor_max","initial_layer_min_bead_width", - "make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size" ,"notes", - "wipe_tower_cone_angle", "wipe_tower_extra_spacing","wipe_tower_max_purge_speed", - "wipe_tower_wall_type", "wipe_tower_extra_rib_length", "wipe_tower_rib_width", "wipe_tower_fillet_wall", - "wipe_tower_filament", "wiping_volumes_extruders","wipe_tower_bridging", "wipe_tower_extra_flow","single_extruder_multi_material_priming", - "wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic", - "hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", - "small_area_infill_flow_compensation", "small_area_infill_flow_compensation_model", - "enable_wrapping_detection", - "seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "scarf_joint_speed", "scarf_joint_flow_ratio", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls", "scarf_overhang_threshold", - "interlocking_beam", "interlocking_orientation", "interlocking_beam_layer_count", "interlocking_depth", "interlocking_boundary_avoidance", "interlocking_beam_width","calib_flowrate_topinfill_special_order", - // Z Anti-Aliasing (ZAA) - "zaa_enabled", "zaa_minimize_perimeter_height", "zaa_dont_alternate_fill_direction", "zaa_min_z", "zaa_region_disable", "ironing_expansion", + "wipe_tower_no_sparse_layers", + "compatible_printers", + "compatible_printers_condition", + "inherits", + "flush_into_infill", + "flush_into_objects", + "flush_into_support", + "tree_support_branch_angle", + "tree_support_angle_slow", + "tree_support_wall_count", + "tree_support_top_rate", + "tree_support_branch_distance", + "tree_support_tip_diameter", + "tree_support_branch_diameter", + "tree_support_branch_diameter_angle", + "detect_narrow_internal_solid_infill", + "gcode_add_line_number", + "enable_arc_fitting", + "precise_z_height", + "infill_combination", + "infill_combination_max_layer_height", /*"adaptive_layer_height",*/ + "support_bottom_interface_spacing", + "enable_overhang_speed", + "slowdown_for_curled_perimeters", + "overhang_1_4_speed", + "overhang_2_4_speed", + "overhang_3_4_speed", + "overhang_4_4_speed", + "initial_layer_infill_speed", + "only_one_wall_top", + "timelapse_type", + "wall_generator", + "wall_transition_length", + "wall_transition_filter_deviation", + "wall_transition_angle", + "wall_distribution_count", + "min_feature_size", + "min_bead_width", + "post_process", + "min_length_factor", + "small_perimeter_speed", + "small_perimeter_threshold", + "bridge_angle", + "internal_bridge_angle", + "filter_out_gap_fill", + "travel_acceleration", + "inner_wall_acceleration", + "min_width_top_surface", + "default_jerk", + "outer_wall_jerk", + "inner_wall_jerk", + "infill_jerk", + "top_surface_jerk", + "initial_layer_jerk", + "travel_jerk", + "default_junction_deviation", + "top_solid_infill_flow_ratio", + "bottom_solid_infill_flow_ratio", + "only_one_wall_first_layer", + "print_flow_ratio", + "seam_gap", + "set_other_flow_ratios", + "first_layer_flow_ratio", + "outer_wall_flow_ratio", + "inner_wall_flow_ratio", + "overhang_flow_ratio", + "sparse_infill_flow_ratio", + "internal_solid_infill_flow_ratio", + "gap_fill_flow_ratio", + "support_flow_ratio", + "support_interface_flow_ratio", + "role_based_wipe_speed", + "wipe_speed", + "accel_to_decel_enable", + "accel_to_decel_factor", + "wipe_on_loops", + "wipe_before_external_loop", + "bridge_density", + "internal_bridge_density", + "precise_outer_wall", + "bridge_acceleration", + "sparse_infill_acceleration", + "internal_solid_infill_acceleration", + "tree_support_auto_brim", + "tree_support_brim_width", + "gcode_comments", + "gcode_label_objects", + "initial_layer_travel_speed", + "exclude_object", + "slow_down_layers", + "infill_anchor", + "infill_anchor_max", + "initial_layer_min_bead_width", + "make_overhang_printable", + "make_overhang_printable_angle", + "make_overhang_printable_hole_size", + "notes", + "wipe_tower_cone_angle", + "wipe_tower_extra_spacing", + "wipe_tower_max_purge_speed", + "wipe_tower_wall_type", + "wipe_tower_extra_rib_length", + "wipe_tower_rib_width", + "wipe_tower_fillet_wall", + "wipe_tower_filament", + "wiping_volumes_extruders", + "wipe_tower_bridging", + "wipe_tower_extra_flow", + "single_extruder_multi_material_priming", + "wipe_tower_rotation_angle", + "tree_support_branch_distance_organic", + "tree_support_branch_diameter_organic", + "tree_support_branch_angle_organic", + "hole_to_polyhole", + "hole_to_polyhole_threshold", + "hole_to_polyhole_twisted", + "mmu_segmented_region_max_width", + "mmu_segmented_region_interlocking_depth", + "small_area_infill_flow_compensation", + "small_area_infill_flow_compensation_model", + "enable_wrapping_detection", + "seam_slope_type", + "seam_slope_conditional", + "scarf_angle_threshold", + "scarf_joint_speed", + "scarf_joint_flow_ratio", + "seam_slope_start_height", + "seam_slope_entire_loop", + "seam_slope_min_length", + "seam_slope_steps", + "seam_slope_inner_walls", + "scarf_overhang_threshold", + "interlocking_beam", + "interlocking_orientation", + "interlocking_beam_layer_count", + "interlocking_depth", + "interlocking_boundary_avoidance", + "interlocking_beam_width", + "calib_flowrate_topinfill_special_order", + // Z Anti-Aliasing (ZAA) + "zaa_enabled", + "zaa_minimize_perimeter_height", + "zaa_dont_alternate_fill_direction", + "zaa_min_z", + "ironing_expansion", }; static std::vector s_Preset_filament_options {/*"filament_colour", */ "default_filament_colour", "required_nozzle_HRC", "filament_diameter", "pellet_flow_coefficient", "volumetric_speed_coefficients", "filament_type", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 9325d202df..b0013db24d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2201,7 +2201,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) // Z-Contouring for (PrintObject *obj : m_objects) { - bool need_contouring = need_slicing_objects.count(obj) != 0 && obj->config().zaa_enabled; + bool need_contouring = need_slicing_objects.count(obj) != 0 && obj->need_z_contouring(); if (need_contouring) { obj->contour_z(); } else { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 4d2bca8dd1..eeb74a87ed 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -496,6 +496,7 @@ private: void prepare_infill(); void infill(); void ironing(); + bool need_z_contouring() const; void contour_z(); void generate_support_material(); void estimate_curled_extrusions(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f7fae90001..94be78eebc 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4063,13 +4063,6 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0)); - def = this->add("zaa_region_disable", coBool); - def->label = L("Disable Z contouring for region"); - def->category = L("Quality"); - def->tooltip = L("Disable Z contouring for this specific region"); - def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); - def = this->add("zaa_enabled", coBool); def->label = L("Z contouring enabled"); def->category = L("Quality"); @@ -4080,12 +4073,13 @@ void PrintConfigDef::init_fff_params() def = this->add("zaa_minimize_perimeter_height", coFloat); def->label = L("Minimize wall height angle"); def->category = L("Quality"); - def->tooltip = L("Reduce top surface perimeter heights to match height of edge for perimeters less than this angle. Set 0 to disable."); + def->tooltip = L("Reduce height of top surface perimeters to match height of model edge. " + "Effects perimeters whose slope is less than this angle in degrees. Reasonable value is 35. Set 0 to disable."); def->sidetext = L("°"); def->min = 0; def->max = 90; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(35)); + def->set_default_value(new ConfigOptionFloat(0)); def = this->add("zaa_dont_alternate_fill_direction", coBool); def->label = L("Don't alternate fill direction"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index c6a79a8b66..1d272abc86 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1046,166 +1046,90 @@ PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE( PrintRegionConfig, - ((ConfigOptionInts, print_extruder_id)) - ((ConfigOptionStrings, print_extruder_variant)) - ((ConfigOptionInt, bottom_shell_layers)) - ((ConfigOptionFloat, bottom_shell_thickness)) - ((ConfigOptionFloat, bridge_angle)) - ((ConfigOptionFloat, internal_bridge_angle)) // ORCA: Internal bridge angle override - ((ConfigOptionFloat, bridge_flow)) - ((ConfigOptionFloat, internal_bridge_flow)) - ((ConfigOptionFloat, bridge_speed)) - ((ConfigOptionFloatOrPercent, internal_bridge_speed)) - ((ConfigOptionEnum, ensure_vertical_shell_thickness)) - ((ConfigOptionPercent, top_surface_density)) - ((ConfigOptionPercent, bottom_surface_density)) - ((ConfigOptionEnum, top_surface_pattern)) - ((ConfigOptionEnum, bottom_surface_pattern)) - ((ConfigOptionEnum, internal_solid_infill_pattern)) - ((ConfigOptionFloatOrPercent, outer_wall_line_width)) - ((ConfigOptionFloat, outer_wall_speed)) - ((ConfigOptionFloat, infill_direction)) - ((ConfigOptionFloat, 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, lateral_lattice_angle_1)) - ((ConfigOptionFloat, lateral_lattice_angle_2)) - ((ConfigOptionFloat, infill_overhang_angle)) - ((ConfigOptionBool, align_infill_direction_to_model)) - ((ConfigOptionString, extra_solid_infills)) - ((ConfigOptionEnum, fuzzy_skin)) - ((ConfigOptionFloat, fuzzy_skin_thickness)) - ((ConfigOptionFloat, fuzzy_skin_point_distance)) - ((ConfigOptionBool, fuzzy_skin_first_layer)) - ((ConfigOptionEnum, fuzzy_skin_noise_type)) - ((ConfigOptionEnum, fuzzy_skin_mode)) - ((ConfigOptionFloat, fuzzy_skin_scale)) - ((ConfigOptionInt, fuzzy_skin_octaves)) - ((ConfigOptionFloat, fuzzy_skin_persistence)) - ((ConfigOptionFloat, gap_infill_speed)) - ((ConfigOptionInt, sparse_infill_filament)) - ((ConfigOptionFloatOrPercent, sparse_infill_line_width)) - ((ConfigOptionPercent, infill_wall_overlap)) - ((ConfigOptionPercent, top_bottom_infill_wall_overlap)) - ((ConfigOptionFloat, sparse_infill_speed)) - ((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)) + ((ConfigOptionInts, print_extruder_id))((ConfigOptionStrings, print_extruder_variant))((ConfigOptionInt, bottom_shell_layers))( + (ConfigOptionFloat, bottom_shell_thickness))((ConfigOptionFloat, bridge_angle))( + (ConfigOptionFloat, internal_bridge_angle)) // ORCA: Internal bridge angle override + ((ConfigOptionFloat, bridge_flow))((ConfigOptionFloat, internal_bridge_flow))((ConfigOptionFloat, bridge_speed))( + (ConfigOptionFloatOrPercent, internal_bridge_speed))((ConfigOptionEnum, + ensure_vertical_shell_thickness))((ConfigOptionPercent, top_surface_density))( + (ConfigOptionPercent, bottom_surface_density))((ConfigOptionEnum, top_surface_pattern))( + (ConfigOptionEnum, bottom_surface_pattern))((ConfigOptionEnum, internal_solid_infill_pattern))( + (ConfigOptionFloatOrPercent, outer_wall_line_width))((ConfigOptionFloat, outer_wall_speed))((ConfigOptionFloat, infill_direction))( + (ConfigOptionFloat, 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, lateral_lattice_angle_1))( + (ConfigOptionFloat, lateral_lattice_angle_2))((ConfigOptionFloat, infill_overhang_angle))((ConfigOptionBool, + align_infill_direction_to_model))(( + ConfigOptionString, extra_solid_infills))((ConfigOptionEnum, fuzzy_skin))((ConfigOptionFloat, fuzzy_skin_thickness))( + (ConfigOptionFloat, fuzzy_skin_point_distance))((ConfigOptionBool, fuzzy_skin_first_layer))((ConfigOptionEnum, + fuzzy_skin_noise_type))( + (ConfigOptionEnum, fuzzy_skin_mode))((ConfigOptionFloat, fuzzy_skin_scale))((ConfigOptionInt, fuzzy_skin_octaves))( + (ConfigOptionFloat, fuzzy_skin_persistence))((ConfigOptionFloat, gap_infill_speed))((ConfigOptionInt, sparse_infill_filament))( + (ConfigOptionFloatOrPercent, sparse_infill_line_width))((ConfigOptionPercent, infill_wall_overlap))( + (ConfigOptionPercent, top_bottom_infill_wall_overlap))((ConfigOptionFloat, sparse_infill_speed))( + (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)) - ((ConfigOptionInt, fill_multiline)) + ((ConfigOptionFloatOrPercent, infill_combination_max_layer_height))((ConfigOptionInt, fill_multiline)) // Ironing options - ((ConfigOptionEnum, ironing_type)) - ((ConfigOptionEnum, ironing_pattern)) - ((ConfigOptionPercent, ironing_flow)) - ((ConfigOptionFloat, ironing_spacing)) - ((ConfigOptionFloat, ironing_inset)) - ((ConfigOptionFloat, ironing_direction)) - ((ConfigOptionFloat, ironing_speed)) - ((ConfigOptionFloat, ironing_angle)) - ((ConfigOptionBool, ironing_angle_fixed)) - ((ConfigOptionFloat, ironing_expansion)) + ((ConfigOptionEnum, ironing_type))((ConfigOptionEnum, ironing_pattern))((ConfigOptionPercent, ironing_flow))( + (ConfigOptionFloat, ironing_spacing))((ConfigOptionFloat, ironing_inset))((ConfigOptionFloat, + ironing_direction))((ConfigOptionFloat, ironing_speed))( + (ConfigOptionFloat, ironing_angle))((ConfigOptionBool, ironing_angle_fixed))((ConfigOptionFloat, ironing_expansion)) // Filament Ironing - ((ConfigOptionPercentsNullable, filament_ironing_flow)) - ((ConfigOptionFloatsNullable, filament_ironing_spacing)) - ((ConfigOptionFloatsNullable, filament_ironing_inset)) - ((ConfigOptionFloatsNullable, filament_ironing_speed)) + ((ConfigOptionPercentsNullable, filament_ironing_flow))((ConfigOptionFloatsNullable, filament_ironing_spacing))( + (ConfigOptionFloatsNullable, filament_ironing_inset))((ConfigOptionFloatsNullable, filament_ironing_speed)) // Detect bridging perimeters - ((ConfigOptionBool, detect_overhang_wall)) - ((ConfigOptionInt, wall_filament)) - ((ConfigOptionFloatOrPercent, inner_wall_line_width)) - ((ConfigOptionFloat, inner_wall_speed)) + ((ConfigOptionBool, detect_overhang_wall))((ConfigOptionInt, wall_filament))((ConfigOptionFloatOrPercent, inner_wall_line_width))( + (ConfigOptionFloat, inner_wall_speed)) // Total number of perimeters. - ((ConfigOptionInt, wall_loops)) - ((ConfigOptionBool, alternate_extra_wall)) - ((ConfigOptionFloat, minimum_sparse_infill_area)) - ((ConfigOptionInt, solid_infill_filament)) - ((ConfigOptionFloatOrPercent, internal_solid_infill_line_width)) - ((ConfigOptionFloat, internal_solid_infill_speed)) + ((ConfigOptionInt, wall_loops))((ConfigOptionBool, alternate_extra_wall))((ConfigOptionFloat, minimum_sparse_infill_area))( + (ConfigOptionInt, solid_infill_filament))((ConfigOptionFloatOrPercent, + internal_solid_infill_line_width))((ConfigOptionFloat, internal_solid_infill_speed)) // Detect thin walls. - ((ConfigOptionBool, detect_thin_wall)) - ((ConfigOptionFloatOrPercent, top_surface_line_width)) - ((ConfigOptionInt, top_shell_layers)) - ((ConfigOptionFloat, top_shell_thickness)) - ((ConfigOptionFloat, top_surface_speed)) - //BBS - ((ConfigOptionBool, enable_overhang_speed)) - ((ConfigOptionFloatOrPercent, overhang_1_4_speed)) - ((ConfigOptionFloatOrPercent, overhang_2_4_speed)) - ((ConfigOptionFloatOrPercent, overhang_3_4_speed)) - ((ConfigOptionFloatOrPercent, overhang_4_4_speed)) - ((ConfigOptionBool, only_one_wall_top)) + ((ConfigOptionBool, detect_thin_wall))((ConfigOptionFloatOrPercent, top_surface_line_width))((ConfigOptionInt, top_shell_layers))( + (ConfigOptionFloat, top_shell_thickness))((ConfigOptionFloat, top_surface_speed)) + // BBS + ((ConfigOptionBool, enable_overhang_speed))((ConfigOptionFloatOrPercent, overhang_1_4_speed))( + (ConfigOptionFloatOrPercent, overhang_2_4_speed))((ConfigOptionFloatOrPercent, overhang_3_4_speed))( + (ConfigOptionFloatOrPercent, overhang_4_4_speed))((ConfigOptionBool, only_one_wall_top)) - //SoftFever - ((ConfigOptionFloatOrPercent, min_width_top_surface)) - ((ConfigOptionBool, only_one_wall_first_layer)) - ((ConfigOptionFloat, print_flow_ratio)) - ((ConfigOptionFloatOrPercent, seam_gap)) - ((ConfigOptionBool, role_based_wipe_speed)) - ((ConfigOptionFloatOrPercent, wipe_speed)) - ((ConfigOptionBool, wipe_on_loops)) - ((ConfigOptionBool, wipe_before_external_loop)) - ((ConfigOptionEnum, wall_infill_order)) - ((ConfigOptionBool, precise_outer_wall)) - ((ConfigOptionPercent, bridge_density)) - ((ConfigOptionFloat, filter_out_gap_fill)) - ((ConfigOptionFloatOrPercent, small_perimeter_speed)) - ((ConfigOptionFloat, small_perimeter_threshold)) - ((ConfigOptionFloat, top_solid_infill_flow_ratio)) - ((ConfigOptionFloat, bottom_solid_infill_flow_ratio)) - ((ConfigOptionFloatOrPercent, infill_anchor)) - ((ConfigOptionFloatOrPercent, infill_anchor_max)) + // SoftFever + ((ConfigOptionFloatOrPercent, min_width_top_surface))((ConfigOptionBool, only_one_wall_first_layer))( + (ConfigOptionFloat, print_flow_ratio))((ConfigOptionFloatOrPercent, seam_gap))((ConfigOptionBool, role_based_wipe_speed))( + (ConfigOptionFloatOrPercent, wipe_speed))((ConfigOptionBool, wipe_on_loops))((ConfigOptionBool, wipe_before_external_loop))( + (ConfigOptionEnum, wall_infill_order))((ConfigOptionBool, precise_outer_wall))(( + ConfigOptionPercent, bridge_density))((ConfigOptionFloat, filter_out_gap_fill))((ConfigOptionFloatOrPercent, small_perimeter_speed))( + (ConfigOptionFloat, small_perimeter_threshold))((ConfigOptionFloat, top_solid_infill_flow_ratio))( + (ConfigOptionFloat, bottom_solid_infill_flow_ratio))((ConfigOptionFloatOrPercent, infill_anchor))((ConfigOptionFloatOrPercent, + infill_anchor_max)) // Orca - ((ConfigOptionBool, make_overhang_printable)) - ((ConfigOptionBool, extra_perimeters_on_overhangs)) - ((ConfigOptionBool, slowdown_for_curled_perimeters)) - ((ConfigOptionBool, hole_to_polyhole)) - ((ConfigOptionFloatOrPercent, hole_to_polyhole_threshold)) - ((ConfigOptionBool, hole_to_polyhole_twisted)) - ((ConfigOptionBool, overhang_reverse)) - ((ConfigOptionBool, overhang_reverse_internal_only)) - ((ConfigOptionFloatOrPercent, overhang_reverse_threshold)) - ((ConfigOptionEnum, counterbore_hole_bridging)) - ((ConfigOptionEnum, wall_sequence)) - ((ConfigOptionBool, is_infill_first)) - ((ConfigOptionBool, small_area_infill_flow_compensation)) - ((ConfigOptionEnum, wall_direction)) + ((ConfigOptionBool, make_overhang_printable))((ConfigOptionBool, extra_perimeters_on_overhangs))( + (ConfigOptionBool, slowdown_for_curled_perimeters))((ConfigOptionBool, hole_to_polyhole))( + (ConfigOptionFloatOrPercent, hole_to_polyhole_threshold))((ConfigOptionBool, hole_to_polyhole_twisted))( + (ConfigOptionBool, overhang_reverse))((ConfigOptionBool, overhang_reverse_internal_only))((ConfigOptionFloatOrPercent, + overhang_reverse_threshold))( + (ConfigOptionEnum, counterbore_hole_bridging))((ConfigOptionEnum, wall_sequence))( + (ConfigOptionBool, is_infill_first))((ConfigOptionBool, small_area_infill_flow_compensation))((ConfigOptionEnum, + wall_direction)) // Orca: other flow ratios (available for overriding, if set_other_flow_ratios is enabled) - ((ConfigOptionFloat, first_layer_flow_ratio)) - ((ConfigOptionFloat, outer_wall_flow_ratio)) - ((ConfigOptionFloat, inner_wall_flow_ratio)) - ((ConfigOptionFloat, overhang_flow_ratio)) - ((ConfigOptionFloat, sparse_infill_flow_ratio)) - ((ConfigOptionFloat, internal_solid_infill_flow_ratio)) - ((ConfigOptionFloat, gap_fill_flow_ratio)) + ((ConfigOptionFloat, first_layer_flow_ratio))((ConfigOptionFloat, outer_wall_flow_ratio))((ConfigOptionFloat, inner_wall_flow_ratio))( + (ConfigOptionFloat, overhang_flow_ratio))((ConfigOptionFloat, sparse_infill_flow_ratio))( + (ConfigOptionFloat, internal_solid_infill_flow_ratio))((ConfigOptionFloat, gap_fill_flow_ratio)) // Orca: seam slopes - ((ConfigOptionEnum, seam_slope_type)) - ((ConfigOptionBool, seam_slope_conditional)) - ((ConfigOptionInt, scarf_angle_threshold)) - ((ConfigOptionFloatOrPercent, seam_slope_start_height)) - ((ConfigOptionBool, seam_slope_entire_loop)) - ((ConfigOptionFloat, seam_slope_min_length)) - ((ConfigOptionInt, seam_slope_steps)) - ((ConfigOptionBool, seam_slope_inner_walls)) - ((ConfigOptionFloatOrPercent, scarf_joint_speed)) - ((ConfigOptionFloat, scarf_joint_flow_ratio)) - ((ConfigOptionPercent, scarf_overhang_threshold)) + ((ConfigOptionEnum, seam_slope_type))((ConfigOptionBool, seam_slope_conditional))( + (ConfigOptionInt, scarf_angle_threshold))((ConfigOptionFloatOrPercent, seam_slope_start_height))( + (ConfigOptionBool, seam_slope_entire_loop))((ConfigOptionFloat, seam_slope_min_length))((ConfigOptionInt, seam_slope_steps))( + (ConfigOptionBool, seam_slope_inner_walls))((ConfigOptionFloatOrPercent, scarf_joint_speed))( + (ConfigOptionFloat, scarf_joint_flow_ratio))((ConfigOptionPercent, scarf_overhang_threshold)) // Z Anti-Aliasing (aka Z Contouring) - ((ConfigOptionBool, zaa_region_disable)) - ((ConfigOptionFloat, zaa_minimize_perimeter_height)) -) + ((ConfigOptionBool, zaa_enabled))((ConfigOptionFloat, zaa_minimize_perimeter_height))) PRINT_CONFIG_CLASS_DEFINE( MachineEnvelopeConfig, diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index e4d47c065e..b21355697f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -25,6 +25,7 @@ #include "format.hpp" #include "AABBTreeLines.hpp" +#include #include #include #include @@ -715,6 +716,21 @@ void PrintObject::ironing() } } +bool PrintObject::need_z_contouring() const +{ + if (this->config().zaa_enabled) { + return true; + } + + size_t num_regions = this->num_printing_regions(); + for (size_t region_id = 0; region_id < num_regions; region_id++) { + if (this->printing_region(region_id).config().zaa_enabled) + return true; + } + + return false; +} + void PrintObject::contour_z() { if (!this->set_started(posContouring)) { diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 6251c544b2..9d30757f23 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -100,24 +100,63 @@ std::map> SettingsFactory::OBJECT_C }} }; -std::map> SettingsFactory::PART_CATEGORY_SETTINGS= -{ - { L("Quality"), {{"ironing_type", "",8},{"ironing_flow", "",9},{"ironing_spacing", "",10},{"ironing_inset", "", 11},{"bridge_flow", "",11},{"make_overhang_printable", "",11},{"bridge_density", "", 1}, - {"ironing_expansion", "", 14}, - {"zaa_enabled", "", 1}, {"zaa_region_disable", "", 2}, {"zaa_minimize_perimeter_height", "", 3}, {"zaa_dont_alternate_fill_direction", "", 4}, {"zaa_min_z", "", 5} - }}, - { L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},{"top_surface_density", L("Top Surface Density"),1}, - {"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},{"bottom_surface_density", L("Bottom Surface Density"),1}, - {"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"lateral_lattice_angle_1", "",1},{"lateral_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}, - {"align_infill_direction_to_model", "", 1}, - {"extra_solid_infills", "", 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}, - {"bridge_speed", "",11}, {"gap_infill_speed", "",12}, {"internal_bridge_speed", "", 13} - }} -}; +std::map> SettingsFactory::PART_CATEGORY_SETTINGS = + {{L("Quality"), + {{"ironing_type", "", 8}, + {"ironing_flow", "", 9}, + {"ironing_spacing", "", 10}, + {"ironing_inset", "", 11}, + {"bridge_flow", "", 11}, + {"make_overhang_printable", "", 11}, + {"bridge_density", "", 1}, + {"ironing_expansion", "", 14}, + {"zaa_enabled", "", 1}, + {"zaa_minimize_perimeter_height", "", 2}, + {"zaa_dont_alternate_fill_direction", "", 3}, + {"zaa_min_z", "", 4}}}, + {L("Strength"), + {{"wall_loops", "", 1}, + {"top_shell_layers", L("Top Solid Layers"), 1}, + {"top_shell_thickness", L("Top Minimum Shell Thickness"), 1}, + {"top_surface_density", L("Top Surface Density"), 1}, + {"bottom_shell_layers", L("Bottom Solid Layers"), 1}, + {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"), 1}, + {"bottom_surface_density", L("Bottom Surface Density"), 1}, + {"sparse_infill_density", "", 1}, + {"sparse_infill_pattern", "", 1}, + {"lateral_lattice_angle_1", "", 1}, + {"lateral_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}, + {"align_infill_direction_to_model", "", 1}, + {"extra_solid_infills", "", 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}, + {"bridge_speed", "", 11}, + {"gap_infill_speed", "", 12}, + {"internal_bridge_speed", "", 13}}}}; std::vector SettingsFactory::get_options(const bool is_part) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 1ab1b82c03..5cef6eaa78 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2342,9 +2342,7 @@ void TabPrint::build() optgroup = page->new_optgroup("Z Contouring", L"param_advanced"); optgroup->append_single_option_line("zaa_enabled"); - optgroup->append_single_option_line("zaa_region_disable"); optgroup->append_single_option_line("zaa_minimize_perimeter_height"); - optgroup->append_single_option_line("zaa_dont_alternate_fill_direction"); optgroup->append_single_option_line("zaa_min_z"); optgroup->append_single_option_line("ironing_expansion"); From fb675e3193da9229e5f2c9a1309c40170ebb16f9 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Thu, 26 Mar 2026 05:47:00 -0700 Subject: [PATCH 21/28] Fix z-height calculation to correctly handle case when height == min_z --- src/libslic3r/ContourZ.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp index 49c6cd5d68..ee4f8d1dbd 100644 --- a/src/libslic3r/ContourZ.cpp +++ b/src/libslic3r/ContourZ.cpp @@ -101,13 +101,16 @@ static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh & } } - if (d > max_up + 0.03 || d < min_down) { - d = 0; - } else { - if (d > max_up) { - d = max_up; - } - } + if (d < -height || d > max_up + 0.03) { + // this point is too far from the mesh edge, probably because this is not a top surface. Do not contour it. + d = 0; + } + + if (d < min_down) { + d = min_down; + } else if (d > max_up) { + d = max_up; + } if (is_perimeter(path.role()) && d > 0) { // do not increase height of perimeters as this may create an appearance of a seam From 160173b9ca41d91ddb49b47f7442e4511c3749a2 Mon Sep 17 00:00:00 2001 From: Aleksandr Dobkin Date: Thu, 26 Mar 2026 06:25:04 -0700 Subject: [PATCH 22/28] Avoid extra z travel moves --- src/libslic3r/GCode.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 831d30ac42..fa8cab67aa 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6155,8 +6155,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (!m_last_pos_defined || m_last_pos.to_point() != first_point || m_need_change_layer_lift_z || slope_need_z_travel) { const bool _last_pos_undefined = !m_last_pos_defined; - gcode += this->travel_to(path.first_point(), path.role(), "move to first " + description + " point", - sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio)); + double z = DBL_MAX; + if (sloped != nullptr) { + z = get_sloped_z(sloped->slope_begin.z_ratio); + } else if (path.z_contoured && !path.polyline.lines().empty()) { + z = unscale_(path.polyline.lines().begin()->a.z()) + m_nominal_z; + } + + gcode += this->travel_to(first_point, path.role(), "move to first " + description + " point", z); m_need_change_layer_lift_z = false; // Orca: ensure Z matches planned layer height @@ -6167,7 +6173,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (path.z_contoured && !path.polyline.lines().empty()) { double current_z = m_writer.get_position().z(); - ; double first_z = unscale_(path.polyline.lines().begin()->a.z()) + m_nominal_z; if (GCodeFormatter::quantize_xyzf(first_z) != GCodeFormatter::quantize_xyzf(current_z)) { gcode += m_writer.travel_to_z(first_z, "set Z for contouring", true); From 24172e6898e9385f9001f73258b244e90cb7a1ea Mon Sep 17 00:00:00 2001 From: SoftFever Date: Tue, 28 Apr 2026 17:20:59 +0800 Subject: [PATCH 23/28] fix missing settings after merging --- src/libslic3r/Preset.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 0c368282fc..caa8c495c0 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1016,6 +1016,7 @@ static std::vector s_Preset_print_options{ "internal_bridge_flow", "elefant_foot_compensation", "elefant_foot_compensation_layers", + "elefant_foot_layers_density", "xy_contour_compensation", "xy_hole_compensation", "resolution", @@ -1068,7 +1069,10 @@ static std::vector s_Preset_print_options{ "min_feature_size", "min_bead_width", "post_process", + "process_change_extrusion_role_gcode", "min_length_factor", + "wall_maximum_resolution", + "wall_maximum_deviation", "small_perimeter_speed", "small_perimeter_threshold", "bridge_angle", @@ -1117,6 +1121,8 @@ static std::vector s_Preset_print_options{ "gcode_comments", "gcode_label_objects", "initial_layer_travel_speed", + "initial_layer_travel_acceleration", + "initial_layer_travel_jerk", "exclude_object", "slow_down_layers", "infill_anchor", From 2957508b4ad42e9ecc536b1dd5f1a6fdd362f263 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Tue, 28 Apr 2026 18:46:56 +0800 Subject: [PATCH 24/28] Fixed a bug where the scarf seam wouldn't work properly when ZAA was disabled. --- src/libslic3r/GCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0483c236ee..487eefb560 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -6248,7 +6248,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, gcode += m_writer.travel_to_z(first_z, "set Z for contouring", true); } } - if (!path.z_contoured) { + if (!path.z_contoured && sloped == nullptr) { double current_z = m_writer.get_position().z(); if (GCodeFormatter::quantize_xyzf(current_z) != GCodeFormatter::quantize_xyzf(m_nominal_z)) { gcode += this->writer().travel_to_z(m_nominal_z, "reset Z after contouring", true); From 1acab58d6030509dd63b971ea6b5a721713d36e2 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Tue, 28 Apr 2026 18:47:56 +0800 Subject: [PATCH 25/28] Fixed a bug where `zaa_dont_alternate_fill_direction` still had an effect even when ZAA is disabled. --- src/libslic3r/Fill/Fill.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index badd2bd78a..59b926e370 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -1218,7 +1218,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); f->layer_id = this->id(); - f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction; + f->dont_alternate_fill_direction = this->object()->config().zaa_enabled && this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->angle = surface_fill.params.angle; f->fixed_angle = surface_fill.params.fixed_angle; @@ -1419,7 +1419,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers. - f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction; + f->dont_alternate_fill_direction = this->object()->config().zaa_enabled && this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->angle = surface_fill.params.angle; f->fixed_angle = surface_fill.params.fixed_angle; @@ -1592,7 +1592,7 @@ void Layer::make_ironing() std::unique_ptr f = std::unique_ptr(Fill::new_from_type(f_pattern)); f->set_bounding_box(this->object()->bounding_box()); f->layer_id = this->id(); - f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction; + f->dont_alternate_fill_direction = this->object()->config().zaa_enabled && this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->overlap = 0; for (size_t i = 0; i < by_extruder.size();) { @@ -1605,7 +1605,7 @@ void Layer::make_ironing() f = std::unique_ptr(Fill::new_from_type(f_pattern)); f->set_bounding_box(this->object()->bounding_box()); f->layer_id = this->id(); - f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction; + f->dont_alternate_fill_direction = this->object()->config().zaa_enabled && this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->overlap = 0; } From ae6729248c317b2399148292318dc46d4b09ba08 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Tue, 28 Apr 2026 19:57:14 +0800 Subject: [PATCH 26/28] revert pure format changes --- src/libslic3r/PrintConfig.hpp | 212 +++++++++++++++++++++++----------- 1 file changed, 142 insertions(+), 70 deletions(-) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index cd4dce20b0..8da739a9d5 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1049,89 +1049,161 @@ PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE( PrintRegionConfig, - ((ConfigOptionInts, print_extruder_id))((ConfigOptionStrings, print_extruder_variant))((ConfigOptionInt, bottom_shell_layers))( - (ConfigOptionFloat, bottom_shell_thickness))((ConfigOptionFloat, bridge_angle))( - (ConfigOptionFloat, internal_bridge_angle)) // ORCA: Internal bridge angle override - ((ConfigOptionFloat, bridge_flow))((ConfigOptionFloat, internal_bridge_flow))((ConfigOptionFloat, bridge_speed))( - (ConfigOptionFloatOrPercent, internal_bridge_speed))((ConfigOptionEnum, - ensure_vertical_shell_thickness))((ConfigOptionPercent, top_surface_density))( - (ConfigOptionPercent, bottom_surface_density))((ConfigOptionEnum, top_surface_pattern))( - (ConfigOptionEnum, bottom_surface_pattern))((ConfigOptionEnum, internal_solid_infill_pattern))( - (ConfigOptionFloatOrPercent, outer_wall_line_width))((ConfigOptionFloat, outer_wall_speed))((ConfigOptionFloat, infill_direction))( - (ConfigOptionFloat, 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, lateral_lattice_angle_1))( - (ConfigOptionFloat, lateral_lattice_angle_2))((ConfigOptionFloat, infill_overhang_angle))((ConfigOptionBool, - align_infill_direction_to_model))(( - ConfigOptionString, extra_solid_infills))((ConfigOptionEnum, fuzzy_skin))((ConfigOptionFloat, fuzzy_skin_thickness))( - (ConfigOptionFloat, fuzzy_skin_point_distance))((ConfigOptionBool, fuzzy_skin_first_layer))((ConfigOptionEnum, - fuzzy_skin_noise_type))( - (ConfigOptionEnum, fuzzy_skin_mode))((ConfigOptionFloat, fuzzy_skin_scale))((ConfigOptionInt, fuzzy_skin_octaves))( - (ConfigOptionFloat, fuzzy_skin_persistence))((ConfigOptionFloat, gap_infill_speed))((ConfigOptionInt, sparse_infill_filament))( - (ConfigOptionFloatOrPercent, sparse_infill_line_width))((ConfigOptionPercent, infill_wall_overlap))( - (ConfigOptionPercent, top_bottom_infill_wall_overlap))((ConfigOptionFloat, sparse_infill_speed))( - (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)) + ((ConfigOptionInts, print_extruder_id)) + ((ConfigOptionStrings, print_extruder_variant)) + ((ConfigOptionInt, bottom_shell_layers)) + ((ConfigOptionFloat, bottom_shell_thickness)) + ((ConfigOptionFloat, bridge_angle)) + ((ConfigOptionFloat, internal_bridge_angle)) // ORCA: Internal bridge angle override + ((ConfigOptionFloat, bridge_flow)) + ((ConfigOptionFloat, internal_bridge_flow)) + ((ConfigOptionFloat, bridge_speed)) + ((ConfigOptionFloatOrPercent, internal_bridge_speed)) + ((ConfigOptionEnum, ensure_vertical_shell_thickness)) + ((ConfigOptionPercent, top_surface_density)) + ((ConfigOptionPercent, bottom_surface_density)) + ((ConfigOptionEnum, top_surface_pattern)) + ((ConfigOptionEnum, bottom_surface_pattern)) + ((ConfigOptionEnum, internal_solid_infill_pattern)) + ((ConfigOptionFloatOrPercent, outer_wall_line_width)) + ((ConfigOptionFloat, outer_wall_speed)) + ((ConfigOptionFloat, infill_direction)) + ((ConfigOptionFloat, 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, lateral_lattice_angle_1)) + ((ConfigOptionFloat, lateral_lattice_angle_2)) + ((ConfigOptionFloat, infill_overhang_angle)) + ((ConfigOptionBool, align_infill_direction_to_model)) + ((ConfigOptionString, extra_solid_infills)) + ((ConfigOptionEnum, fuzzy_skin)) + ((ConfigOptionFloat, fuzzy_skin_thickness)) + ((ConfigOptionFloat, fuzzy_skin_point_distance)) + ((ConfigOptionBool, fuzzy_skin_first_layer)) + ((ConfigOptionEnum, fuzzy_skin_noise_type)) + ((ConfigOptionEnum, fuzzy_skin_mode)) + ((ConfigOptionFloat, fuzzy_skin_scale)) + ((ConfigOptionInt, fuzzy_skin_octaves)) + ((ConfigOptionFloat, fuzzy_skin_persistence)) + ((ConfigOptionFloat, gap_infill_speed)) + ((ConfigOptionInt, sparse_infill_filament)) + ((ConfigOptionFloatOrPercent, sparse_infill_line_width)) + ((ConfigOptionPercent, infill_wall_overlap)) + ((ConfigOptionPercent, top_bottom_infill_wall_overlap)) + ((ConfigOptionFloat, sparse_infill_speed)) + ((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))((ConfigOptionInt, fill_multiline)) + ((ConfigOptionFloatOrPercent, infill_combination_max_layer_height)) + ((ConfigOptionInt, fill_multiline)) // Ironing options - ((ConfigOptionEnum, ironing_type))((ConfigOptionEnum, ironing_pattern))((ConfigOptionPercent, ironing_flow))( - (ConfigOptionFloat, ironing_spacing))((ConfigOptionFloat, ironing_inset))((ConfigOptionFloat, - ironing_direction))((ConfigOptionFloat, ironing_speed))( - (ConfigOptionFloat, ironing_angle))((ConfigOptionBool, ironing_angle_fixed))((ConfigOptionFloat, ironing_expansion)) + ((ConfigOptionEnum, ironing_type)) + ((ConfigOptionEnum, ironing_pattern)) + ((ConfigOptionPercent, ironing_flow)) + ((ConfigOptionFloat, ironing_spacing)) + ((ConfigOptionFloat, ironing_inset)) + ((ConfigOptionFloat, ironing_direction)) + ((ConfigOptionFloat, ironing_speed)) + ((ConfigOptionFloat, ironing_angle)) + ((ConfigOptionBool, ironing_angle_fixed)) // Filament Ironing - ((ConfigOptionPercentsNullable, filament_ironing_flow))((ConfigOptionFloatsNullable, filament_ironing_spacing))( - (ConfigOptionFloatsNullable, filament_ironing_inset))((ConfigOptionFloatsNullable, filament_ironing_speed)) + ((ConfigOptionPercentsNullable, filament_ironing_flow)) + ((ConfigOptionFloatsNullable, filament_ironing_spacing)) + ((ConfigOptionFloatsNullable, filament_ironing_inset)) + ((ConfigOptionFloatsNullable, filament_ironing_speed)) // Detect bridging perimeters - ((ConfigOptionBool, detect_overhang_wall))((ConfigOptionInt, wall_filament))((ConfigOptionFloatOrPercent, inner_wall_line_width))( - (ConfigOptionFloat, inner_wall_speed)) + ((ConfigOptionBool, detect_overhang_wall)) + ((ConfigOptionInt, wall_filament)) + ((ConfigOptionFloatOrPercent, inner_wall_line_width)) + ((ConfigOptionFloat, inner_wall_speed)) // Total number of perimeters. - ((ConfigOptionInt, wall_loops))((ConfigOptionBool, alternate_extra_wall))((ConfigOptionFloat, minimum_sparse_infill_area))( - (ConfigOptionInt, solid_infill_filament))((ConfigOptionFloatOrPercent, - internal_solid_infill_line_width))((ConfigOptionFloat, internal_solid_infill_speed)) + ((ConfigOptionInt, wall_loops)) + ((ConfigOptionBool, alternate_extra_wall)) + ((ConfigOptionFloat, minimum_sparse_infill_area)) + ((ConfigOptionInt, solid_infill_filament)) + ((ConfigOptionFloatOrPercent, internal_solid_infill_line_width)) + ((ConfigOptionFloat, internal_solid_infill_speed)) // Detect thin walls. - ((ConfigOptionBool, detect_thin_wall))((ConfigOptionFloatOrPercent, top_surface_line_width))((ConfigOptionInt, top_shell_layers))( - (ConfigOptionFloat, top_shell_thickness))((ConfigOptionFloat, top_surface_speed)) - // BBS - ((ConfigOptionBool, enable_overhang_speed))((ConfigOptionFloatOrPercent, overhang_1_4_speed))( - (ConfigOptionFloatOrPercent, overhang_2_4_speed))((ConfigOptionFloatOrPercent, overhang_3_4_speed))( - (ConfigOptionFloatOrPercent, overhang_4_4_speed))((ConfigOptionBool, only_one_wall_top)) + ((ConfigOptionBool, detect_thin_wall)) + ((ConfigOptionFloatOrPercent, top_surface_line_width)) + ((ConfigOptionInt, top_shell_layers)) + ((ConfigOptionFloat, top_shell_thickness)) + ((ConfigOptionFloat, top_surface_speed)) + //BBS + ((ConfigOptionBool, enable_overhang_speed)) + ((ConfigOptionFloatOrPercent, overhang_1_4_speed)) + ((ConfigOptionFloatOrPercent, overhang_2_4_speed)) + ((ConfigOptionFloatOrPercent, overhang_3_4_speed)) + ((ConfigOptionFloatOrPercent, overhang_4_4_speed)) + ((ConfigOptionBool, only_one_wall_top)) - // SoftFever - ((ConfigOptionFloatOrPercent, min_width_top_surface))((ConfigOptionBool, only_one_wall_first_layer))( - (ConfigOptionFloat, print_flow_ratio))((ConfigOptionFloatOrPercent, seam_gap))((ConfigOptionBool, role_based_wipe_speed))( - (ConfigOptionFloatOrPercent, wipe_speed))((ConfigOptionBool, wipe_on_loops))((ConfigOptionBool, wipe_before_external_loop))( - (ConfigOptionEnum, wall_infill_order))((ConfigOptionBool, precise_outer_wall))(( - ConfigOptionPercent, bridge_density))((ConfigOptionFloat, filter_out_gap_fill))((ConfigOptionFloatOrPercent, small_perimeter_speed))( - (ConfigOptionFloat, small_perimeter_threshold))((ConfigOptionFloat, top_solid_infill_flow_ratio))( - (ConfigOptionFloat, bottom_solid_infill_flow_ratio))((ConfigOptionFloatOrPercent, infill_anchor))((ConfigOptionFloatOrPercent, - infill_anchor_max)) + //SoftFever + ((ConfigOptionFloatOrPercent, min_width_top_surface)) + ((ConfigOptionBool, only_one_wall_first_layer)) + ((ConfigOptionFloat, print_flow_ratio)) + ((ConfigOptionFloatOrPercent, seam_gap)) + ((ConfigOptionBool, role_based_wipe_speed)) + ((ConfigOptionFloatOrPercent, wipe_speed)) + ((ConfigOptionBool, wipe_on_loops)) + ((ConfigOptionBool, wipe_before_external_loop)) + ((ConfigOptionEnum, wall_infill_order)) + ((ConfigOptionBool, precise_outer_wall)) + ((ConfigOptionPercent, bridge_density)) + ((ConfigOptionFloat, filter_out_gap_fill)) + ((ConfigOptionFloatOrPercent, small_perimeter_speed)) + ((ConfigOptionFloat, small_perimeter_threshold)) + ((ConfigOptionFloat, top_solid_infill_flow_ratio)) + ((ConfigOptionFloat, bottom_solid_infill_flow_ratio)) + ((ConfigOptionFloatOrPercent, infill_anchor)) + ((ConfigOptionFloatOrPercent, infill_anchor_max)) // Orca - ((ConfigOptionBool, make_overhang_printable))((ConfigOptionBool, extra_perimeters_on_overhangs))( - (ConfigOptionBool, slowdown_for_curled_perimeters))((ConfigOptionBool, hole_to_polyhole))( - (ConfigOptionFloatOrPercent, hole_to_polyhole_threshold))((ConfigOptionBool, hole_to_polyhole_twisted))( - (ConfigOptionBool, overhang_reverse))((ConfigOptionBool, overhang_reverse_internal_only))((ConfigOptionFloatOrPercent, - overhang_reverse_threshold))( - (ConfigOptionEnum, counterbore_hole_bridging))((ConfigOptionEnum, wall_sequence))( - (ConfigOptionBool, is_infill_first))((ConfigOptionBool, small_area_infill_flow_compensation))((ConfigOptionEnum, - wall_direction)) + ((ConfigOptionBool, make_overhang_printable)) + ((ConfigOptionBool, extra_perimeters_on_overhangs)) + ((ConfigOptionBool, slowdown_for_curled_perimeters)) + ((ConfigOptionBool, hole_to_polyhole)) + ((ConfigOptionFloatOrPercent, hole_to_polyhole_threshold)) + ((ConfigOptionBool, hole_to_polyhole_twisted)) + ((ConfigOptionBool, overhang_reverse)) + ((ConfigOptionBool, overhang_reverse_internal_only)) + ((ConfigOptionFloatOrPercent, overhang_reverse_threshold)) + ((ConfigOptionEnum, counterbore_hole_bridging)) + ((ConfigOptionEnum, wall_sequence)) + ((ConfigOptionBool, is_infill_first)) + ((ConfigOptionBool, small_area_infill_flow_compensation)) + ((ConfigOptionEnum, wall_direction)) // Orca: other flow ratios (available for overriding, if set_other_flow_ratios is enabled) - ((ConfigOptionFloat, first_layer_flow_ratio))((ConfigOptionFloat, outer_wall_flow_ratio))((ConfigOptionFloat, inner_wall_flow_ratio))( - (ConfigOptionFloat, overhang_flow_ratio))((ConfigOptionFloat, sparse_infill_flow_ratio))( - (ConfigOptionFloat, internal_solid_infill_flow_ratio))((ConfigOptionFloat, gap_fill_flow_ratio)) + ((ConfigOptionFloat, first_layer_flow_ratio)) + ((ConfigOptionFloat, outer_wall_flow_ratio)) + ((ConfigOptionFloat, inner_wall_flow_ratio)) + ((ConfigOptionFloat, overhang_flow_ratio)) + ((ConfigOptionFloat, sparse_infill_flow_ratio)) + ((ConfigOptionFloat, internal_solid_infill_flow_ratio)) + ((ConfigOptionFloat, gap_fill_flow_ratio)) // Orca: seam slopes - ((ConfigOptionEnum, seam_slope_type))((ConfigOptionBool, seam_slope_conditional))( - (ConfigOptionInt, scarf_angle_threshold))((ConfigOptionFloatOrPercent, seam_slope_start_height))( - (ConfigOptionBool, seam_slope_entire_loop))((ConfigOptionFloat, seam_slope_min_length))((ConfigOptionInt, seam_slope_steps))( - (ConfigOptionBool, seam_slope_inner_walls))((ConfigOptionFloatOrPercent, scarf_joint_speed))( - (ConfigOptionFloat, scarf_joint_flow_ratio))((ConfigOptionPercent, scarf_overhang_threshold)) - - // Z Anti-Aliasing (aka Z Contouring) + ((ConfigOptionEnum, seam_slope_type)) + ((ConfigOptionBool, seam_slope_conditional)) + ((ConfigOptionInt, scarf_angle_threshold)) + ((ConfigOptionFloatOrPercent, seam_slope_start_height)) + ((ConfigOptionBool, seam_slope_entire_loop)) + ((ConfigOptionFloat, seam_slope_min_length)) + ((ConfigOptionInt, seam_slope_steps)) + ((ConfigOptionBool, seam_slope_inner_walls)) + ((ConfigOptionFloatOrPercent, scarf_joint_speed)) + ((ConfigOptionFloat, scarf_joint_flow_ratio)) + ((ConfigOptionPercent, scarf_overhang_threshold)) + // Orca: Z Anti-Aliasing (aka Z Contouring) ((ConfigOptionBool, zaa_enabled))((ConfigOptionFloat, zaa_minimize_perimeter_height))) PRINT_CONFIG_CLASS_DEFINE( From 20c3c833dffefc29f7c49e954714bf0a3eb339db Mon Sep 17 00:00:00 2001 From: SoftFever Date: Fri, 1 May 2026 19:47:00 +0800 Subject: [PATCH 27/28] fixed an issue that zaa_enabled is redefined in both object and region. move all zaa config to region to keep it consistent --- src/libslic3r/ContourZ.cpp | 2 +- src/libslic3r/Fill/Fill.cpp | 13 +++++++++---- src/libslic3r/PrintConfig.hpp | 12 ++++++------ src/libslic3r/PrintObject.cpp | 4 ---- src/libslic3r/PrintObjectSlice.cpp | 14 ++++++++++++-- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/ContourZ.cpp b/src/libslic3r/ContourZ.cpp index ee4f8d1dbd..dc6cc08f46 100644 --- a/src/libslic3r/ContourZ.cpp +++ b/src/libslic3r/ContourZ.cpp @@ -38,7 +38,7 @@ static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh & Layer *layer = region->layer(); coordf_t mesh_z = layer->print_z + mesh.ground_level(); - coordf_t min_z = layer->object()->config().zaa_min_z; + coordf_t min_z = region->region().config().zaa_min_z; const Points3 &points = path.polyline.points; double resolution_mm = 0.1; diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 59b926e370..2b1a5d86cf 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -1218,7 +1218,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); f->layer_id = this->id(); - f->dont_alternate_fill_direction = this->object()->config().zaa_enabled && this->object()->config().zaa_dont_alternate_fill_direction; + { + const auto &rcfg = m_regions[surface_fill.region_id]->region().config(); + f->dont_alternate_fill_direction = rcfg.zaa_enabled && rcfg.zaa_dont_alternate_fill_direction; + } f->z = this->print_z; f->angle = surface_fill.params.angle; f->fixed_angle = surface_fill.params.fixed_angle; @@ -1419,7 +1422,10 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc std::unique_ptr f = std::unique_ptr(Fill::new_from_type(surface_fill.params.pattern)); f->set_bounding_box(bbox); f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers. - f->dont_alternate_fill_direction = this->object()->config().zaa_enabled && this->object()->config().zaa_dont_alternate_fill_direction; + { + const auto &rcfg = m_regions[surface_fill.region_id]->region().config(); + f->dont_alternate_fill_direction = rcfg.zaa_enabled && rcfg.zaa_dont_alternate_fill_direction; + } f->z = this->print_z; f->angle = surface_fill.params.angle; f->fixed_angle = surface_fill.params.fixed_angle; @@ -1592,12 +1598,12 @@ void Layer::make_ironing() std::unique_ptr f = std::unique_ptr(Fill::new_from_type(f_pattern)); f->set_bounding_box(this->object()->bounding_box()); f->layer_id = this->id(); - f->dont_alternate_fill_direction = this->object()->config().zaa_enabled && this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->overlap = 0; for (size_t i = 0; i < by_extruder.size();) { // Find span of regions equivalent to the ironing operation. IroningParams &ironing_params = by_extruder[i]; + f->dont_alternate_fill_direction = ironing_params.layerm->region().config().zaa_enabled && ironing_params.layerm->region().config().zaa_dont_alternate_fill_direction; // Create the filler object. if( f_pattern != ironing_params.pattern ) { @@ -1605,7 +1611,6 @@ void Layer::make_ironing() f = std::unique_ptr(Fill::new_from_type(f_pattern)); f->set_bounding_box(this->object()->bounding_box()); f->layer_id = this->id(); - f->dont_alternate_fill_direction = this->object()->config().zaa_enabled && this->object()->config().zaa_dont_alternate_fill_direction; f->z = this->print_z; f->overlap = 0; } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8da739a9d5..55586a8adb 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1036,11 +1036,6 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionInt, interlocking_depth)) ((ConfigOptionInt, interlocking_boundary_avoidance)) - // Z Anti-Aliasing (aka Z Contouring) - ((ConfigOptionBool, zaa_enabled)) - ((ConfigOptionBool, zaa_dont_alternate_fill_direction)) - ((ConfigOptionFloat, zaa_min_z)) - // Orca: internal use only ((ConfigOptionBool, calib_flowrate_topinfill_special_order)) // ORCA: special flag for flow rate calibration ) @@ -1203,8 +1198,13 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, scarf_joint_speed)) ((ConfigOptionFloat, scarf_joint_flow_ratio)) ((ConfigOptionPercent, scarf_overhang_threshold)) + // Orca: Z Anti-Aliasing (aka Z Contouring) - ((ConfigOptionBool, zaa_enabled))((ConfigOptionFloat, zaa_minimize_perimeter_height))) + ((ConfigOptionBool, zaa_enabled)) + ((ConfigOptionBool, zaa_dont_alternate_fill_direction)) + ((ConfigOptionFloat, zaa_min_z)) + ((ConfigOptionFloat, zaa_minimize_perimeter_height)) + ) PRINT_CONFIG_CLASS_DEFINE( MachineEnvelopeConfig, diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b67f8a7ced..4e41fa7151 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -718,10 +718,6 @@ void PrintObject::ironing() bool PrintObject::need_z_contouring() const { - if (this->config().zaa_enabled) { - return true; - } - size_t num_regions = this->num_printing_regions(); for (size_t region_id = 0; region_id < num_regions; region_id++) { if (this->printing_region(region_id).config().zaa_enabled) diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index a30a95fc7e..abeb420e00 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -35,8 +35,18 @@ LayerPtrs new_layers( coordf_t lo = object_layers[i_layer]; coordf_t hi = object_layers[i_layer + 1]; coordf_t slice_z = 0.5 * (lo + hi); - if (print_object->config().zaa_enabled) { - coordf_t z_offset = print_object->config().zaa_min_z; + bool zaa_active = false; + coordf_t z_offset = 0.0; + size_t num_regions = print_object->num_printing_regions(); + for (size_t rid = 0; rid < num_regions; ++rid) { + const auto &rcfg = print_object->printing_region(rid).config(); + if (rcfg.zaa_enabled) { + if (!zaa_active || rcfg.zaa_min_z < z_offset) + z_offset = rcfg.zaa_min_z; + zaa_active = true; + } + } + if (zaa_active) { slice_z = lo + z_offset; if (slice_z < lo || slice_z > hi) { throw RuntimeError("Bad min Z value"); From 3d0b24f94db0698f5b38f824e65bf56e18a26d2a Mon Sep 17 00:00:00 2001 From: SoftFever Date: Fri, 1 May 2026 21:25:58 +0800 Subject: [PATCH 28/28] Tweak UI 1. Move ZAA options to comExpert 2. Expose zaa_dont_alternate_fill_direction to the UI 3. Remove ironing_expansion from UI as it's not implemented --- src/libslic3r/PrintConfig.cpp | 10 +++++----- src/slic3r/GUI/ConfigManipulation.cpp | 4 ++++ src/slic3r/GUI/Tab.cpp | 4 +++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0239590a6e..a36489cccb 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4118,14 +4118,14 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = -100; def->max = 100; - def->mode = comAdvanced; + def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0)); def = this->add("zaa_enabled", coBool); def->label = L("Z contouring enabled"); def->category = L("Quality"); def->tooltip = L("Enable Z-layer contouring (aka Z-layer anti-aliasing)"); - def->mode = comAdvanced; + def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); def = this->add("zaa_minimize_perimeter_height", coFloat); @@ -4136,14 +4136,14 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("°"); def->min = 0; def->max = 90; - def->mode = comAdvanced; + def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0)); def = this->add("zaa_dont_alternate_fill_direction", coBool); def->label = L("Don't alternate fill direction"); def->category = L("Quality"); def->tooltip = L("Disable alternating fill direction when using Z contouring"); - def->mode = comAdvanced; + def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); def = this->add("zaa_min_z", coFloat); @@ -4153,7 +4153,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("mm"); def->min = 0; def->max = 100; - def->mode = comAdvanced; + def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.05)); def = this->add("layer_change_gcode", coString); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 0a67c841ec..238b6ac690 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -801,6 +801,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line("ironing_speed", has_ironing || has_support_ironing); + bool has_zaa = config->opt_bool("zaa_enabled"); + for (auto el : {"zaa_minimize_perimeter_height", "zaa_min_z", "zaa_dont_alternate_fill_direction", "ironing_expansion"}) + toggle_line(el, has_zaa); + bool have_sequential_printing = (config->opt_enum("print_sequence") == PrintSequence::ByObject); // for (auto el : { "extruder_clearance_radius", "extruder_clearance_height_to_rod", "extruder_clearance_height_to_lid" }) // toggle_field(el, have_sequential_printing); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d94bb35d2d..868b537879 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2324,7 +2324,9 @@ void TabPrint::build() optgroup->append_single_option_line("zaa_enabled"); optgroup->append_single_option_line("zaa_minimize_perimeter_height"); optgroup->append_single_option_line("zaa_min_z"); - optgroup->append_single_option_line("ironing_expansion"); + optgroup->append_single_option_line("zaa_dont_alternate_fill_direction"); + // Orca: it's not used yet, so hide it in UI for now + // optgroup->append_single_option_line("ironing_expansion"); optgroup = page->new_optgroup(L("Wall generator"), L"param_wall_generator"); optgroup->append_single_option_line("wall_generator", "quality_settings_wall_generator");