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/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 5f9591452f..e42b08e016 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..dc6cc08f46 --- /dev/null +++ b/src/libslic3r/ContourZ.cpp @@ -0,0 +1,236 @@ +#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 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; +} + +static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionPath &path) +{ + 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 = region->region().config().zaa_min_z; + + const Points3 &points = path.polyline.points; + double resolution_mm = 0.1; + + coordf_t height = layer->height; + + double minimize_perimeter_height_angle = region->region().config().zaa_minimize_perimeter_height; + + Pointf3s contoured_points; + bool was_contoured = false; + + 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(); + + if (num_segments == 0) { + continue; + } + + 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); + } + + 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) { + 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 < -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 + 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(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); + } + } + + 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) { + if (!contains(roles, extr->role())) { + continue; + } + + contour_extrusion_entity(region, mesh, extr); + } +} + +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}); + } +} +} // 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 61d2cb9086..36955a19bd 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; } } @@ -266,8 +286,9 @@ 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) { if (!p1.polyline.is_valid()) { std::swap(this->paths.front().polyline.points, p2.polyline.points); @@ -411,16 +432,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 +462,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 +485,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 +652,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 f9ead17ed5..2b1a5d86cf 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -1218,6 +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(); + { + 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; @@ -1418,6 +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. + { + 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; @@ -1595,6 +1603,7 @@ void Layer::make_ironing() 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 ) { 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 284d40f240..35af30514a 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" @@ -22,10 +23,12 @@ #include "Time.hpp" #include "GCode/ExtrusionProcessor.hpp" #include +#include #include #include #include #include +#include #include #include #include @@ -5521,12 +5524,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; } @@ -5603,11 +5606,15 @@ 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, + 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 + ExtrusionLoop loop = loop_ref; bool is_hole = (loop.loop_role() & elrHole) == elrHole; @@ -5682,13 +5689,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 @@ -5735,7 +5743,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); @@ -5771,7 +5779,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou 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. @@ -5829,10 +5837,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 } } @@ -5842,8 +5852,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; @@ -5857,8 +5869,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(); @@ -5870,19 +5882,20 @@ 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); + gcode += m_writer.extrude_to_xy(this->point_to_gcode(pt), 0, "move inwards before travel", true); } return gcode; } -std::string GCode::extrude_multi_path(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; - + //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 @@ -5899,8 +5912,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 @@ -5911,13 +5924,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(); } @@ -5925,7 +5940,10 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string 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); @@ -5938,7 +5956,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, 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; @@ -5946,17 +5964,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(); @@ -5999,11 +6017,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); @@ -6034,7 +6052,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"); @@ -6200,14 +6218,20 @@ 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) { + 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; - 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 if (!slope_need_z_travel && (_last_pos_undefined || m_need_change_layer_lift_z)) { const std::string z_sync_comment = _last_pos_undefined ? @@ -6217,6 +6241,19 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, m_need_change_layer_lift_z = false; } + 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 && 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); + } + } // if needed, write the gcode_label_objects_end then gcode_label_objects_start // should be already done by travel_to, but just in case @@ -6803,10 +6840,12 @@ 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()) { + 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; if (line_length < EPSILON) @@ -6821,16 +6860,35 @@ 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, + GCodeWriter::full_gcode_comment ? tempDescription : ""); + + } 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, @@ -6849,7 +6907,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; @@ -6907,14 +6965,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) @@ -6928,7 +6986,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()); @@ -7010,9 +7068,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); @@ -7893,6 +7968,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 { @@ -7910,6 +7992,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 1971415286..fb2a546890 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -221,10 +221,12 @@ 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; + 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; } @@ -383,7 +385,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(); @@ -391,13 +394,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(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, + 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. @@ -584,7 +594,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..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,35 +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) { 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 @@ -121,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)) { @@ -144,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 @@ -161,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 @@ -186,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); @@ -219,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]; @@ -229,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; @@ -251,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; @@ -271,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; } @@ -283,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; @@ -304,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}; } @@ -359,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); @@ -418,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; @@ -452,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/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..5e39ee9f46 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1514,7 +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; } - current.foot_pt = loop.paths[current.path_idx].polyline.points[current.segment_idx]; + current.foot_pt = loop.paths[current.path_idx].polyline.points[current.segment_idx].to_point(); return current; }; @@ -1527,7 +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; - ExtrusionLoop::ClosestPathPoint closest_point{0,0,loop.paths[0].polyline.points[0]}; + 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/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index ef6ff54e5e..270de27a2c 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -929,6 +929,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) @@ -938,7 +943,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 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/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 028ea2aafa..d8240702b2 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -224,17 +224,28 @@ 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()); } + + 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; static const constexpr int Dim = 3; - using Scalar = Vec3crd::Scalar; + using Scalar = coord_t; }; class Linef @@ -243,6 +254,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; @@ -256,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; } @@ -263,6 +279,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; @@ -270,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/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index fbf898234e..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() { @@ -471,6 +462,75 @@ bool MultiPoint3::remove_duplicate_points() return false; } +// Douglas-Peucker simplification for 3D points +Points3 MultiPoint3::_douglas_peucker(const Points3& pts, double tolerance) +{ + 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 + } + return result_pts; +} + BoundingBox get_extents(const MultiPoint &mp) { return BoundingBox(mp.points); @@ -514,4 +574,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..de386b501c 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -119,18 +119,50 @@ 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 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, 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); + 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(); } - void translate(double x, double y); - void translate(const Point& vector); 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 e05b422353..0769fb76de 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; @@ -441,7 +442,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); @@ -475,11 +476,13 @@ 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; + 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) { - 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; } } @@ -674,11 +677,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++) { - if (lines_two.distance_from_lines(path_one.polyline.points[pt_idx]) < 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++) { - if (lines_one.distance_from_lines(path_two.polyline.points[pt_idx]) < 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,7 +1041,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 || @@ -1044,7 +1053,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++) { - Point p = overhang_region.front().polyline[i]; + 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 80c4d9815a..9a2c2b14cd 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -1,6 +1,8 @@ #include "Point.hpp" +#include "Exception.hpp" #include "Line.hpp" #include "MultiPoint.hpp" +#include "Polyline.hpp" #include "Int128.hpp" #include "BoundingBox.hpp" #include @@ -257,4 +259,91 @@ 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.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; + 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 + throw RuntimeError("Point3::projection_onto(MultiPoint3) not implemented yet"); + 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..039f361eaa 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,12 @@ 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); +Points3 to_points3(const Points& 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 +269,118 @@ 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 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 + 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))); + } + 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()); @@ -300,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 b30564f3c1..98a5366ecf 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; @@ -639,15 +661,383 @@ 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; } +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) +{ + 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(); + 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) { + this->points.emplace_back((last_point + v * (distance / sqrt(lsqr))).cast()); + last_point_inserted = true; + break; + } + distance -= sqrt(lsqr); + } + + // 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()) { + // 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) +{ + this->points = MultiPoint3::_douglas_peucker(this->points, tolerance); + this->fitting_result.clear(); +} + +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 +{ + 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 { + // BBS: spilit 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; + + 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); + // append_fitting_result_after_append_points(); + + // BBS: don't need to append same point + if (!this->empty() && this->last_point() == point) + return; + MultiPoint3::append(point); + append_fitting_result_after_append_points(); +} + +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 { + // 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()}); + } + // 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) +{ + // BBS: don't need to append same point + if (!this->empty() && this->first_point() == point) + return; + 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 +{ + if (this->points.empty()) return; + + // 0 judge whether the point is on the polyline + int index = this->find_point(point); + if (index != -1) { + // 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; + } + + // 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(); + 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(); + } + } + + // 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); + 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(Point3(point, p1->last_point().z())); + this->split_at_index(line_idx + 1, &temp, p2); + p2->append_before(Point3(point, p2->first_point().z())); + } +} + +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(this->first_point()); + *p2 = *this; + } else if (is_approx(length, this->length(), SCALED_EPSILON)) { + p2->clear(); + p2->append(this->last_point()); + *p1 = *this; + } else { + // 1 find the line to split at + size_t line_idx = 0; + double acc_length = 0; + 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, l.b, (length - acc_length) / current_length); + break; + } + acc_length += current_length; + line_idx++; + } + + // 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(p); + this->split_at_index(line_idx + 1, &temp, p2); + 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 bff65f7a2f..88671391db 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,63 @@ 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 + void 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: + 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; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index df2d997a8b..35d780d99b 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -974,16 +974,74 @@ 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", @@ -995,54 +1053,220 @@ static std::vector s_Preset_print_options { "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", "elefant_foot_layers_density", "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", + "elefant_foot_layers_density", + "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", "process_change_extrusion_role_gcode", - "min_length_factor", "wall_maximum_resolution", "wall_maximum_deviation", - "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", "initial_layer_travel_acceleration", "initial_layer_travel_jerk", "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", + "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", + "process_change_extrusion_role_gcode", + "min_length_factor", + "wall_maximum_resolution", + "wall_maximum_deviation", + "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", + "initial_layer_travel_acceleration", + "initial_layer_travel_jerk", + "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 b5fe490c9f..f1ce2a4747 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2213,6 +2213,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->need_z_contouring(); + 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++) { @@ -2251,6 +2262,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)) @@ -2630,7 +2643,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. @@ -2688,7 +2701,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. @@ -4093,7 +4106,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]; @@ -4946,8 +4960,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 b98811c8e3..eeb74a87ed 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,8 @@ private: void prepare_infill(); void infill(); void ironing(); + bool need_z_contouring() const; + void contour_z(); void generate_support_material(); void estimate_curled_extrusions(); void simplify_extrusion_path(); @@ -642,14 +644,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 +688,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 = 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 = comExpert; + 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 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 = 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 = comExpert; + 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 = comExpert; + 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 af483f1c9e..55586a8adb 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1198,7 +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)) + ((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 a08a7d74b0..4e41fa7151 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" @@ -8,6 +10,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" @@ -22,7 +25,11 @@ #include "format.hpp" #include "AABBTreeLines.hpp" +#include #include +#include +#include +#include #include #include #include @@ -709,6 +716,65 @@ void PrintObject::ironing() } } +bool PrintObject::need_z_contouring() const +{ + 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)) { + 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"); + } + + ModelInstance *inst = m_model_object->instances.front(); + Point center_offset = this->center_offset(); + Geometry::Transformation trans = inst->get_transformation(); + + 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()); + + sla::IndexedMesh imesh(mesh); + imesh.ground_level_offset(-z); + + 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() { @@ -1370,15 +1436,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 2d67ac0939..abeb420e00 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,23 @@ 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); + 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"); + } + } 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 e7201a7df6..75e04ad4aa 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 48e3f4caf2..61484730ea 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/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}; 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/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 89110b0432..fd3569e2ca 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -2062,7 +2062,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(); @@ -2078,7 +2078,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(); @@ -2096,7 +2096,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/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/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index ead0d0debb..0ee4a76c62 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -98,22 +98,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} - }}, - { 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/LibVGCode/LibVGCodeWrapper.cpp b/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp index 08e336c119..95720dc436 100644 --- a/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp +++ b/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp @@ -315,7 +315,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(); @@ -331,7 +331,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(); @@ -349,7 +349,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/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6c263d6b54..868b537879 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2320,6 +2320,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_minimize_perimeter_height"); + optgroup->append_single_option_line("zaa_min_z"); + 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"); optgroup->append_single_option_line("wall_transition_angle", "quality_settings_wall_generator#arachne"); 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; }