mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-16 18:12:10 +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:
@@ -471,6 +471,51 @@ bool MultiPoint3::remove_duplicate_points()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Douglas-Peucker simplification for 3D points
|
||||
Points3 MultiPoint3::_douglas_peucker(const Points3 &points, double tolerance)
|
||||
{
|
||||
if (points.size() <= 2) return points;
|
||||
|
||||
// Find the point with maximum distance from line segment
|
||||
double max_dist = 0;
|
||||
size_t max_idx = 0;
|
||||
const Point3 &first = points.front();
|
||||
const Point3 &last = points.back();
|
||||
Line3 line(first, last);
|
||||
|
||||
for (size_t i = 1; i < points.size() - 1; ++i) {
|
||||
// Calculate perpendicular distance to line segment
|
||||
Point3 proj = points[i].projection_onto(line);
|
||||
double dist = points[i].distance_to(proj);
|
||||
if (dist > max_dist) {
|
||||
max_dist = dist;
|
||||
max_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
// If max distance is greater than tolerance, recursively simplify
|
||||
if (max_dist > tolerance) {
|
||||
// Recursive call for first part
|
||||
Points3 left_points(points.begin(), points.begin() + max_idx + 1);
|
||||
Points3 left_result = _douglas_peucker(left_points, tolerance);
|
||||
|
||||
// Recursive call for second part
|
||||
Points3 right_points(points.begin() + max_idx, points.end());
|
||||
Points3 right_result = _douglas_peucker(right_points, tolerance);
|
||||
|
||||
// Concatenate results (avoiding duplicate middle point)
|
||||
Points3 result = left_result;
|
||||
result.insert(result.end(), right_result.begin() + 1, right_result.end());
|
||||
return result;
|
||||
} else {
|
||||
// All points between first and last can be removed
|
||||
Points3 result;
|
||||
result.push_back(first);
|
||||
result.push_back(last);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const MultiPoint &mp)
|
||||
{
|
||||
return BoundingBox(mp.points);
|
||||
@@ -514,4 +559,78 @@ void MultiPoint::symmetric_y(const coord_t &x_axis)
|
||||
}
|
||||
}
|
||||
|
||||
// MultiPoint3 implementations
|
||||
void MultiPoint3::rotate(double cos_angle, double sin_angle)
|
||||
{
|
||||
for (Point3 &pt : this->points) {
|
||||
double cur_x = double(pt(0));
|
||||
double cur_y = double(pt(1));
|
||||
pt(0) = coord_t(round(cos_angle * cur_x - sin_angle * cur_y));
|
||||
pt(1) = coord_t(round(cos_angle * cur_y + sin_angle * cur_x));
|
||||
// Keep Z unchanged
|
||||
}
|
||||
}
|
||||
|
||||
void MultiPoint3::rotate(double angle, const Point3 ¢er)
|
||||
{
|
||||
double s = sin(angle);
|
||||
double c = cos(angle);
|
||||
for (Point3 &pt : points) {
|
||||
Vec3crd v(pt - center);
|
||||
pt(0) = (coord_t)round(double(center(0)) + c * v[0] - s * v[1]);
|
||||
pt(1) = (coord_t)round(double(center(1)) + c * v[1] + s * v[0]);
|
||||
// Keep Z unchanged from original point
|
||||
}
|
||||
}
|
||||
|
||||
int MultiPoint3::find_point(const Point &point) const
|
||||
{
|
||||
for (const Point3 &pt : this->points)
|
||||
if (pt.to_point() == point)
|
||||
return int(&pt - &this->points.front());
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
int MultiPoint3::find_point(const Point &point, double scaled_epsilon) const
|
||||
{
|
||||
if (scaled_epsilon == 0) return this->find_point(point);
|
||||
|
||||
auto dist2_min = std::numeric_limits<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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user