feat: Add Z Anti-Aliasing (ZAA) contouring support (updated) (#12736)
This PR supersedes #12225, which originally proposed this feature but appears inactive. The feature originated from work I developed earlier in [BambuStudio-ZAA](https://github.com/adob/BambuStudio-ZAA), a private fork of Bambu Studio Compared to #12225, I updated the implementation for current upstream and fixed the following issues: - fixed broken tests - removed references to nonplanar directory Reviewers may want to compare against #12225 for earlier discussion/context. ## Summary Port of **Z Anti-Aliasing (ZAA)** from [BambuStudio-ZAA](https://github.com/adob/BambuStudio-ZAA) to OrcaSlicer. ZAA eliminates visible stair-stepping on curved and sloped top surfaces by raycasting each extrusion point against the original 3D mesh and micro-adjusting its Z height to follow the actual surface geometry. The result is visibly smoother domes, chamfers, and shallow slopes — without post-processing. ## How It Works 1. The slicer runs normally, then a **posContouring** step processes each layer 2. `ContourZ.cpp` raycasts every extrusion point vertically against the source mesh 3. Each point's Z is adjusted to the mesh intersection, converting flat `Polyline` paths into `Polyline3` paths with per-point Z coordinates 4. The G-code writer emits the adjusted Z values, so the printer follows the true surface ## Configuration Five new settings under **Print Settings > Quality**: | Setting | Type | Default | Description | |---------|------|---------|-------------| | `zaa_enabled` | bool | off | Master enable/disable switch | | `zaa_min_z` | float | 0.06 mm | Minimum Z layer height; controls slicing plane offset | | `zaa_minimize_perimeter_height` | float | 35° | Reduce perimeter heights on slopes below this angle (0 = disabled) | | `zaa_dont_alternate_fill_direction` | bool | off | Keep fill direction consistent instead of alternating | | `zaa_region_disable` | bool | off | Disable ZAA for a specific print region/material | ## Key Changes - **Core algorithm**: New `src/libslic3r/ContourZ.cpp` (~330 lines) — raycasting engine - **3D geometry**: `Point3`, `Line3`, `Polyline3`, `MultiPoint3` extend existing 2D types - **Arc fitting**: Templated to work with both 2D and 3D geometry - **Pipeline**: `ExtrusionPath::polyline` changed from `Polyline` to `Polyline3`; new `posContouring` step in `PrintObject.cpp` - **G-code**: `GCode.cpp` writes per-point Z when `path.z_contoured` is set - **UI**: ZAA settings exposed in Print Settings > Quality panel - **Documentation**: `docs/ZAA.md` with usage and implementation details 57 files changed, ~1800 insertions, ~200 deletions. ## Test Plan - [ ] Load a model with curved top surfaces (sphere, dome, chamfered box) - [ ] Enable **Z contouring** in Print Settings > Quality - [ ] Slice and verify G-code has varying Z values within contoured layers - [ ] Build on macOS (verified), test on Linux and Windows
This commit is contained in:
@@ -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 <typename LineType>
|
||||
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType>& lines)
|
||||
template<typename LineType>
|
||||
inline AABBTreeIndirect::Tree<LineType::Dim, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
|
||||
const std::vector<LineType>& lines)
|
||||
{
|
||||
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
|
||||
using TreeType = AABBTreeIndirect::Tree<LineType::Dim, typename LineType::Scalar>;
|
||||
// using CoordType = typename TreeType::CoordType;
|
||||
using VectorType = typename TreeType::VectorType;
|
||||
using BoundingBox = typename TreeType::BoundingBox;
|
||||
@@ -303,7 +304,7 @@ namespace AABBTreeLines {
|
||||
|
||||
private:
|
||||
std::vector<LineType> lines;
|
||||
AABBTreeIndirect::Tree<2, Scalar> tree;
|
||||
AABBTreeIndirect::Tree<LineType::Dim, Scalar> tree;
|
||||
|
||||
public:
|
||||
explicit LinesDistancer(const std::vector<LineType>& 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<LineType::Dim, Scalar>& point) const { return point_outside_closed_contours(lines, tree, point); }
|
||||
|
||||
// negative sign means inside
|
||||
template <bool SIGNED_DISTANCE>
|
||||
std::tuple<Floating, size_t, Vec<2, Floating>> distance_from_lines_extra(const Vec<2, Scalar>& point) const
|
||||
template<bool SIGNED_DISTANCE>
|
||||
std::tuple<Floating, size_t, Vec<LineType::Dim, Floating>> distance_from_lines_extra(const Vec<LineType::Dim, Scalar>& 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<Floating>();
|
||||
Vec<LineType::Dim, Floating> nearest_point_out = Vec<LineType::Dim, Floating>::Zero();
|
||||
Vec<LineType::Dim, Floating> p = point.template cast<Floating>();
|
||||
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 <bool SIGNED_DISTANCE>
|
||||
Floating distance_from_lines(const Vec<2, typename LineType::Scalar>& point) const
|
||||
template<bool SIGNED_DISTANCE> Floating distance_from_lines(const Vec<LineType::Dim, typename LineType::Scalar>& point) const
|
||||
{
|
||||
auto [dist, idx, np] = distance_from_lines_extra<SIGNED_DISTANCE>(point);
|
||||
return dist;
|
||||
}
|
||||
|
||||
std::vector<size_t> all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius)
|
||||
{
|
||||
return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast<Floating>(), radius * radius);
|
||||
}
|
||||
|
||||
template <bool sorted>
|
||||
std::vector<std::pair<Vec<2, Scalar>, size_t>> intersections_with_line(const LineType& line) const
|
||||
std::vector<size_t> all_lines_in_radius(const Vec<LineType::Dim, Scalar>& point, Floating radius)
|
||||
{
|
||||
return get_intersections_with_line<sorted, Vec<2, Scalar>>(lines, tree, line);
|
||||
return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast<Floating>(), radius * radius);
|
||||
}
|
||||
|
||||
template<bool sorted> std::vector<std::pair<Vec<LineType::Dim, Scalar>, size_t>> intersections_with_line(const LineType& line) const
|
||||
{
|
||||
return get_intersections_with_line<sorted, Vec<LineType::Dim, Scalar>>(lines, tree, line);
|
||||
}
|
||||
|
||||
const LineType& get_line(size_t line_idx) const { return lines[line_idx]; }
|
||||
|
||||
@@ -236,7 +236,18 @@ class BoundingBox3 : public BoundingBox3Base<Vec3crd>
|
||||
public:
|
||||
BoundingBox3() : BoundingBox3Base<Vec3crd>() {}
|
||||
BoundingBox3(const Vec3crd &pmin, const Vec3crd &pmax) : BoundingBox3Base<Vec3crd>(pmin, pmax) {}
|
||||
BoundingBox3(const Points3& points) : BoundingBox3Base<Vec3crd>(points) {}
|
||||
BoundingBox3(const Points3& points) : BoundingBox3Base<Vec3crd>() {
|
||||
if (!points.empty()) {
|
||||
this->min = points.front();
|
||||
this->max = points.front();
|
||||
for (const auto &p : points) {
|
||||
this->min = this->min.cwiseMin(static_cast<const Vec3crd&>(p));
|
||||
this->max = this->max.cwiseMax(static_cast<const Vec3crd&>(p));
|
||||
}
|
||||
this->defined = true;
|
||||
}
|
||||
}
|
||||
BoundingBox3(const std::vector<Vec3crd>& points) : BoundingBox3Base<Vec3crd>(points) {}
|
||||
};
|
||||
|
||||
class BoundingBoxf : public BoundingBoxBase<Vec2d>
|
||||
|
||||
@@ -100,6 +100,7 @@ set(lisbslic3r_sources
|
||||
CommonDefs.hpp
|
||||
Config.cpp
|
||||
Config.hpp
|
||||
ContourZ.cpp
|
||||
CustomGCode.cpp
|
||||
CustomGCode.hpp
|
||||
CutUtils.cpp
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<ClipperLib::Paths>(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<ClipperLib::Paths>(
|
||||
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<ClipperLib::Paths>(raw_offset_polyline(ClipperUtils::PolylinesProvider(polylines), delta, joinType, miterLimit, end_type))); }
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
236
src/libslic3r/ContourZ.cpp
Normal file
236
src/libslic3r/ContourZ.cpp
Normal file
@@ -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 <cfloat>
|
||||
#include <cmath>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
|
||||
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<const ExtrusionPathSloped*>(extr);
|
||||
if (sloped != nullptr) {
|
||||
throw RuntimeError("ExtrusionPathSloped not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
ExtrusionMultiPath *multipath = dynamic_cast<ExtrusionMultiPath*>(extr);
|
||||
if (multipath != nullptr) {
|
||||
contour_extrusion_multipath(region, mesh, *multipath);
|
||||
return;
|
||||
}
|
||||
|
||||
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(extr);
|
||||
if (path != nullptr) {
|
||||
contour_extrusion_path(region, mesh, *path);
|
||||
return;
|
||||
}
|
||||
|
||||
ExtrusionLoop *loop = dynamic_cast<ExtrusionLoop*>(extr);
|
||||
if (loop != nullptr) {
|
||||
contour_extrusion_loop(region, mesh, *loop);
|
||||
return;
|
||||
}
|
||||
|
||||
const ExtrusionLoopSloped *loop_sloped = dynamic_cast<const ExtrusionLoopSloped*>(extr);
|
||||
if (loop_sloped != nullptr) {
|
||||
throw RuntimeError("ExtrusionLoopSloped not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection *collection = dynamic_cast<ExtrusionEntityCollection*>(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<ExtrusionRole> 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
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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<double>::max();
|
||||
for (const ExtrusionPath &path : this->paths) {
|
||||
std::pair<int, Point> foot_pt_ = foot_pt(path.polyline.points, point);
|
||||
double d2 = (foot_pt_.second - point).cast<double>().squaredNorm();
|
||||
std::pair<int, Point3> 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<double>().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<double>().squaredNorm();
|
||||
double d2_2 = (point - *p2).cast<double>().squaredNorm();
|
||||
Point p1_2d = Point(p1->x(), p1->y());
|
||||
Point p2_2d = Point(p2->x(), p2->y());
|
||||
double d2_1 = (point - p1_2d).cast<double>().squaredNorm();
|
||||
double d2_2 = (point - p2_2d).cast<double>().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<void(const Line &)> handle_line = [slope_max_segment_length, &detailed_poly, &handle_line](const Line &line) {
|
||||
const std::function<void(const Line3 &)> 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<ExtrusionEntity*> 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<double>(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<double> z_diffs;
|
||||
|
||||
ExtrusionPathContoured(Polyline3 &&polyline, const ExtrusionPath &rhs, std::vector<double> &&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<double>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1218,6 +1218,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
std::unique_ptr<Fill> f = std::unique_ptr<Fill>(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<Fill> f = std::unique_ptr<Fill>(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 )
|
||||
{
|
||||
|
||||
@@ -308,7 +308,9 @@ std::pair<float, Point> 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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <algorithm>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
@@ -5521,12 +5524,12 @@ void GCode::set_extruders(const std::vector<unsigned int> &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<EdgeGrid::Grid> 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<double>();
|
||||
Vec2d p2 = paths.front().polyline.points[1].cast<double>();
|
||||
Vec2d p1 = paths.front().polyline.points.front().cast<double>().head<2>();
|
||||
Vec2d p2 = paths.front().polyline.points[1].cast<double>().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<coord_t>();
|
||||
//Point pt = ((nd * nd >= l2) ? (p1+v*0.4): (p1 + 0.2 * v * (nd / sqrt(l2)))).cast<coord_t>();
|
||||
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<const ExtrusionPath*>(&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<double>().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<double>().norm();
|
||||
double dist = (path.polyline.points[i] - path.last_point3()).cast<double>().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<ObjectBy
|
||||
extrusions.emplace_back(ee);
|
||||
if (! extrusions.empty()) {
|
||||
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||
chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
|
||||
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
|
||||
for (const ExtrusionEntity *fill : extrusions) {
|
||||
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(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)
|
||||
|
||||
@@ -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<unsigned int> &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<std::string, std::vector<std::string>> m_placeholder_error_messages;
|
||||
#endif
|
||||
|
||||
Point m_last_pos;
|
||||
Point3 m_last_pos;
|
||||
bool m_last_pos_defined;
|
||||
|
||||
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
|
||||
|
||||
@@ -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()); }
|
||||
|
||||
@@ -27,19 +27,19 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct ExtendedPoint
|
||||
template<int Dim> struct ExtendedPoint
|
||||
{
|
||||
Vec2d position;
|
||||
Eigen::Matrix<double, Dim, 1, Eigen::DontAlign> position;
|
||||
float distance;
|
||||
float curvature;
|
||||
};
|
||||
|
||||
template<bool SCALED_INPUT, bool ADD_INTERSECTIONS, bool PREV_LAYER_BOUNDARY_OFFSET, bool SIGNED_DISTANCE, typename POINTS, typename L>
|
||||
std::vector<ExtendedPoint> estimate_points_properties(const POINTS &input_points,
|
||||
const AABBTreeLines::LinesDistancer<L> &unscaled_prev_layer,
|
||||
float flow_width,
|
||||
float max_line_length = -1.0f,
|
||||
float min_distance = -1.0f)
|
||||
std::vector<ExtendedPoint<L::Dim>> estimate_points_properties(const POINTS& input_points,
|
||||
const AABBTreeLines::LinesDistancer<L>& 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<size_t(size_t,size_t)> get_prev_index = [](size_t idx, size_t count) {
|
||||
@@ -78,35 +78,36 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
|
||||
double min_spacing = flow_width*0.25;
|
||||
|
||||
using AABBScalar = typename AABBTreeLines::LinesDistancer<L>::Scalar;
|
||||
using Vec = Eigen::Matrix<double, L::Dim, 1, Eigen::DontAlign>;
|
||||
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<double>(); };
|
||||
auto maybe_unscale = [](const P& p) -> Vec { return SCALED_INPUT ? unscaled(p) : p.template cast<double>(); };
|
||||
|
||||
std::vector<ExtendedPoint> points;
|
||||
std::vector<ExtendedPoint<L::Dim>> 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<SIGNED_DISTANCE>(start_point.position.cast<AABBScalar>());
|
||||
ExtendedPoint<L::Dim> start_point{maybe_unscale(input_points.front())};
|
||||
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(
|
||||
start_point.position.template cast<AABBScalar>());
|
||||
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<L::Dim> next_point{maybe_unscale(input_points[i])};
|
||||
auto [distance, nearest_line,
|
||||
x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.cast<AABBScalar>());
|
||||
x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(next_point.position.template cast<AABBScalar>());
|
||||
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<true>(
|
||||
L{prev_point.position.cast<AABBScalar>(), next_point.position.cast<AABBScalar>()});
|
||||
const ExtendedPoint<L::Dim>& prev_point = points.back();
|
||||
auto intersections = unscaled_prev_layer.template intersections_with_line<true>(
|
||||
L{prev_point.position.template cast<AABBScalar>(), next_point.position.template cast<AABBScalar>()});
|
||||
for (const auto &intersection : intersections) {
|
||||
ExtendedPoint p{};
|
||||
ExtendedPoint<L::Dim> p{};
|
||||
p.position = intersection.first.template cast<double>();
|
||||
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<ExtendedPoint> estimate_points_properties(const POINTS
|
||||
|
||||
// Segmentation handling
|
||||
if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) {
|
||||
std::vector<ExtendedPoint> new_points;
|
||||
std::vector<ExtendedPoint<L::Dim>> 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<L::Dim>& curr = points[point_idx];
|
||||
const ExtendedPoint<L::Dim>& 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<ExtendedPoint> 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<SIGNED_DISTANCE>(p0.cast<AABBScalar>());
|
||||
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<SIGNED_DISTANCE>(
|
||||
p0.template cast<AABBScalar>());
|
||||
ExtendedPoint<L::Dim> 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<ExtendedPoint> 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<SIGNED_DISTANCE>(p1.cast<AABBScalar>());
|
||||
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<SIGNED_DISTANCE>(
|
||||
p1.template cast<AABBScalar>());
|
||||
ExtendedPoint<L::Dim> 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<ExtendedPoint> estimate_points_properties(const POINTS
|
||||
|
||||
// Maximum line length handling
|
||||
if (max_line_length > 0) {
|
||||
std::vector<ExtendedPoint> new_points;
|
||||
std::vector<ExtendedPoint<L::Dim>> 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<L::Dim>& curr = points[i];
|
||||
const ExtendedPoint<L::Dim>& 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<SIGNED_DISTANCE>(pos.cast<AABBScalar>());
|
||||
ExtendedPoint new_p{};
|
||||
p_x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(pos.template cast<AABBScalar>());
|
||||
ExtendedPoint<L::Dim> new_p{};
|
||||
new_p.position = pos;
|
||||
new_p.distance = float(p_dist + boundary_offset);
|
||||
|
||||
@@ -219,8 +220,8 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
|
||||
float accumulated_distance = 0;
|
||||
std::vector<float> 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<L::Dim>& a = points[point_idx];
|
||||
const ExtendedPoint<L::Dim>& 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<ExtendedPoint> 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<L::Dim>& 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<ExtendedPoint> 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<ExtendedPoint> 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<ExtendedPoint> estimate_points_properties(const POINTS
|
||||
|
||||
struct ProcessedPoint
|
||||
{
|
||||
Point p;
|
||||
Point3 p;
|
||||
float speed = 1.0f;
|
||||
float overlap = 1.0f;
|
||||
};
|
||||
|
||||
class ExtrusionQualityEstimator
|
||||
{
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries;
|
||||
std::unordered_map<const PrintObject*, AABBTreeLines::LinesDistancer<Linef3>> prev_layer_boundaries;
|
||||
std::unordered_map<const PrintObject*, AABBTreeLines::LinesDistancer<Linef3>> next_layer_boundaries;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<CurledLine>> prev_curled_extrusions;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<CurledLine>> 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<Linef>{to_unscaled_linesf(layer->lslices)};
|
||||
next_layer_boundaries[object] = AABBTreeLines::LinesDistancer<Linef3>{to_unscaled_linesf3(layer->lslices)};
|
||||
prev_curled_extrusions[object] = next_curled_extrusions[object];
|
||||
next_curled_extrusions[object] = AABBTreeLines::LinesDistancer<CurledLine>{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<ExtendedPoint> extended_points = estimate_points_properties<true, true, true, true>
|
||||
(path.polyline.points,
|
||||
prev_layer_boundaries[current_object],
|
||||
path.width,
|
||||
-1,
|
||||
smallest_distance_with_lower_speed);
|
||||
std::vector<ExtendedPoint<3>> extended_points =
|
||||
estimate_points_properties<true, true, true, true>(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<ProcessedPoint> 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<double>()))));
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -224,17 +224,28 @@ using CurledLines = std::vector<CurledLine>;
|
||||
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<double>().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<Linef3>;
|
||||
|
||||
BoundingBox get_extents(const Lines &lines);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -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<size_t> 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<double>::max();
|
||||
auto eps2 = scaled_epsilon * scaled_epsilon;
|
||||
int idx_min = -1;
|
||||
for (const Point3 &pt : this->points) {
|
||||
double d2 = (pt.to_point() - point).cast<double>().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<double>::max();
|
||||
auto eps2 = scaled_epsilon * scaled_epsilon;
|
||||
int idx_min = -1;
|
||||
for (const Point3 &pt : this->points) {
|
||||
double d2 = (pt - point).cast<double>().squaredNorm();
|
||||
if (d2 < dist2_min) {
|
||||
idx_min = int(&pt - &this->points.front());
|
||||
dist2_min = d2;
|
||||
}
|
||||
}
|
||||
return (dist2_min < eps2) ? idx_min : -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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, PointInfo, PointHash> 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<Line> 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<false>(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<false>(pt) < limit_distance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
AABBTreeLines::LinesDistancer<Line> 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<false>(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<false>(pt) < limit_distance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1032,7 +1041,7 @@ std::tuple<std::vector<ExtrusionPaths>, 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<true>(path.first_point()) <= 0 ||
|
||||
@@ -1044,7 +1053,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
size_t min_dist_idx = 0;
|
||||
double min_dist = std::numeric_limits<double>::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<true>(p) < min_dist) {
|
||||
min_dist = d;
|
||||
min_dist_idx = i;
|
||||
|
||||
@@ -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 <algorithm>
|
||||
@@ -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<double>().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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<BaseType>;
|
||||
using Points = std::vector<Point, PointsAllocator<Point>>;
|
||||
using PointPtrs = std::vector<Point*>;
|
||||
using PointConstPtrs = std::vector<const Point*>;
|
||||
using Points3 = std::vector<Vec3crd>;
|
||||
using Points3 = std::vector<Point3, PointsAllocator<Point3>>;
|
||||
using Pointfs = std::vector<Vec2d>;
|
||||
using Vec2ds = std::vector<Vec2d>;
|
||||
using Pointf3s = std::vector<Vec3d>;
|
||||
@@ -79,6 +84,12 @@ using Transform2d = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAli
|
||||
using Transform3f = Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>;
|
||||
using Transform3d = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>;
|
||||
|
||||
// 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<float, 4>;
|
||||
// I don't know why Eigen::Transform::Identity() return a const object...
|
||||
template<int N, class T> Transform<N, T> identity() { return Transform<N, T>::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<typename OtherDerived>
|
||||
explicit Point3(const Eigen::MatrixBase<OtherDerived> &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<typename OtherDerived>
|
||||
Point3& operator=(const Eigen::MatrixBase<OtherDerived> &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<double>().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<double>() + t * b.cast<double>()).cast<coord_t>();
|
||||
}
|
||||
|
||||
inline Point3 lerp(const Point3& a, const Point3& b, double t)
|
||||
{
|
||||
assert((t >= -EPSILON) && (t <= 1. + EPSILON));
|
||||
return Point3(((1. - t) * a.cast<double>() + t * b.cast<double>()).cast<coord_t>());
|
||||
}
|
||||
|
||||
// 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<bool IncludeBoundary = false>
|
||||
|
||||
@@ -612,6 +612,28 @@ std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt)
|
||||
return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min);
|
||||
}
|
||||
|
||||
std::pair<int, Point3> 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<double>::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<double>().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<double>();
|
||||
this->points.pop_back();
|
||||
remove_after_index--;
|
||||
if (this->points.empty()) {
|
||||
this->fitting_result.clear();
|
||||
return;
|
||||
}
|
||||
Vec3d v = this->last_point().cast<double>() - last_point;
|
||||
double lsqr = v.squaredNorm();
|
||||
if (lsqr > distance * distance) {
|
||||
this->points.emplace_back((last_point + v * (distance / sqrt(lsqr))).cast<coord_t>());
|
||||
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<double>().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<double>().norm() < min) {
|
||||
p = p_tmp;
|
||||
min = (p - point).cast<double>().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<PathFittingData>& 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<PathFittingData>& 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()});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<int, Point> foot_pt(const Points &polyline, const Point &pt);
|
||||
std::pair<int, Point3> 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<PathFittingData> 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<PathFittingData>& result) const;
|
||||
bool split_fitting_result_after_index(size_t index, Point3& new_startpoint, std::vector<PathFittingData>& result) const;
|
||||
};
|
||||
|
||||
typedef std::vector<Polyline3> Polylines3;
|
||||
|
||||
@@ -974,16 +974,74 @@ bool Preset::has_cali_lines(PresetBundle* preset_bundle)
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::vector<std::string> 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<std::string> 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<std::string> 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<std::string> s_Preset_filament_options {/*"filament_colour", */ "default_filament_colour", "required_nozzle_HRC", "filament_diameter", "pellet_flow_coefficient", "volumetric_speed_coefficients", "filament_type",
|
||||
|
||||
@@ -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<int>(0, int(m_objects.size())),
|
||||
[this, need_slicing_objects](const tbb::blocked_range<int>& 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);
|
||||
|
||||
@@ -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<ExtrusionPaths> 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.); y<maxCorner.y(); y+=scale_(3.)) {
|
||||
path.polyline = { {minCorner.x(), y}, {maxCorner.x(), y} };
|
||||
path.polyline = Polyline3(Polyline{{ {minCorner.x(), y}, {maxCorner.x(), y} }});
|
||||
paths.back().emplace_back(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -4111,6 +4111,51 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = 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.");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 <cstddef>
|
||||
#include <float.h>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <oneapi/tbb/blocked_range.h>
|
||||
#include <oneapi/tbb/concurrent_vector.h>
|
||||
#include <oneapi/tbb/parallel_for.h>
|
||||
@@ -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<double>(center_offset.x()), -unscale<double>(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<size_t>(1, m_layers.size()),
|
||||
[&, this](const tbb::blocked_range<size_t>& 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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1000,7 +1000,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy2(SegmentEndPointFunc
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &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<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near);
|
||||
for (std::pair<size_t, bool> &segment : out) {
|
||||
@@ -1028,6 +1028,11 @@ void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const s
|
||||
entities.swap(out);
|
||||
}
|
||||
|
||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point &start_near)
|
||||
{
|
||||
chain_and_reorder_extrusion_entities(entities, &start_near);
|
||||
}
|
||||
|
||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &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<ExtrusionEntity*> &entitie
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &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<Point, decltype(segment_end_point)>(segment_end_point, extrusion_paths.size(), start_near);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ std::vector<size_t> chain_expolygons(const ExPolygons &input_exploy);
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
|
||||
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain);
|
||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point &start_near);
|
||||
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
|
||||
|
||||
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);
|
||||
|
||||
@@ -1163,7 +1163,7 @@ static void modulate_extrusion_by_overlapping_layers(
|
||||
for (ExtrusionEntity *ee : extrusions_in_out) {
|
||||
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(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<Point, Point>(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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<float>(), b.position.cast<float>(), float((a.position - b.position).norm()),
|
||||
extrusion};
|
||||
|
||||
|
||||
@@ -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<float>(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<float>(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<float>(w) + flow.height() * float(1. - 0.25 * PI));
|
||||
|
||||
Reference in New Issue
Block a user