mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-31 06:42:07 +00:00
feat: Add Z Anti-Aliasing (ZAA) contouring support
Port Z Anti-Aliasing from BambuStudio-ZAA (https://github.com/adob/BambuStudio-ZAA) to OrcaSlicer. ZAA eliminates stair-stepping on curved and sloped top surfaces by raycasting each extrusion point against the original 3D mesh and micro-adjusting Z height to follow the actual surface geometry. Key changes: - Add ContourZ.cpp raycasting algorithm (~330 lines) - Extend geometry with 3D support (Point3, Line3, Polyline3, MultiPoint3) - Template arc fitting for 2D/3D compatibility - Change ExtrusionPath::polyline from Polyline to Polyline3 - Add 5 ZAA config options (zaa_enabled, zaa_min_z, etc.) - Add posContouring pipeline step in PrintObject - Update GCode writer for 3D coordinate output - Add ZAA settings UI in Print Settings > Quality - Add docs/ZAA.md with usage and implementation details ZAA is opt-in and disabled by default. When disabled, the slicing pipeline is unchanged.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user