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:
Matthias Nott
2026-02-09 20:38:46 +01:00
parent cae1567726
commit 963f8d86b7
57 changed files with 1817 additions and 204 deletions

View File

@@ -1,6 +1,7 @@
#include "Point.hpp"
#include "Line.hpp"
#include "MultiPoint.hpp"
#include "Polyline.hpp"
#include "Int128.hpp"
#include "BoundingBox.hpp"
#include <algorithm>
@@ -257,4 +258,80 @@ int cross(const Vec2crd &v1, const Vec2crd &v2)
}
// Point3 utility functions for ZAA (Z Anti-Aliasing)
Polyline to_polyline(const Points &points) { return Polyline(points); }
Polyline3 to_polyline(const Points3 &points) { return Polyline3(points); }
Points to_points(const Points3 &points3) {
Points points2;
points2.reserve(points3.size());
for (const Point3 &pt : points3) {
points2.push_back(pt.to_point());
}
return points2;
}
// Point3 method implementations
void Point3::rotate(double angle, const Point3 &center) {
Vec3crd diff = *this - center;
Point3 temp(diff.x(), diff.y(), diff.z());
temp.rotate(angle);
Vec3crd sum = temp + center;
*this = Point3(sum.x(), sum.y(), sum.z());
}
int Point3::nearest_point_index(const Points &points) const {
return this->to_point().nearest_point_index(points);
}
bool Point3::nearest_point(const Points &points, Point3* point) const {
Point pt2d;
bool result = this->to_point().nearest_point(points, &pt2d);
if (result && point) {
*point = Point3(pt2d, this->z());
}
return result;
}
double Point3::ccw(const Point3 &p1, const Point3 &p2) const {
return this->to_point().ccw(p1.to_point(), p2.to_point());
}
double Point3::ccw(const Line3 &line) const {
// Convert to 2D and use existing Point ccw implementation
Point a2d(line.a.x(), line.a.y());
Point b2d(line.b.x(), line.b.y());
return this->to_point().ccw(Line(a2d, b2d));
}
double Point3::ccw_angle(const Point3 &p1, const Point3 &p2) const {
return this->to_point().ccw_angle(p1.to_point(), p2.to_point());
}
Point3 Point3::projection_onto(const MultiPoint3 &poly) const {
// TODO: Implement proper 3D projection when MultiPoint3 conversion methods are ready
// For now, stub implementation
return *this;
}
Point3 Point3::projection_onto(const Line3 &line) const {
// Project in 2D plane and interpolate Z
Point pt2d = this->to_point();
Point line_a(line.a.x(), line.a.y());
Point line_b(line.b.x(), line.b.y());
Line line2d(line_a, line_b);
Point proj2d = pt2d.projection_onto(line2d);
// Interpolate Z coordinate
double line_len = line.length();
if (line_len < EPSILON) {
return Point3(proj2d, line.a.z());
}
double dist_from_a = (proj2d - line_a).cast<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);
}
}