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

@@ -1215,6 +1215,7 @@ 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();
f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction;
f->z = this->print_z;
f->angle = surface_fill.params.angle;
f->fixed_angle = surface_fill.params.fixed_angle;
@@ -1408,6 +1409,7 @@ 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.
f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction;
f->z = this->print_z;
f->angle = surface_fill.params.angle;
f->fixed_angle = surface_fill.params.fixed_angle;
@@ -1580,6 +1582,7 @@ void Layer::make_ironing()
std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(f_pattern));
f->set_bounding_box(this->object()->bounding_box());
f->layer_id = this->id();
f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction;
f->z = this->print_z;
f->overlap = 0;
for (size_t i = 0; i < by_extruder.size();) {
@@ -1592,6 +1595,7 @@ void Layer::make_ironing()
f = std::unique_ptr<Fill>(Fill::new_from_type(f_pattern));
f->set_bounding_box(this->object()->bounding_box());
f->layer_id = this->id();
f->dont_alternate_fill_direction = this->object()->config().zaa_dont_alternate_fill_direction;
f->z = this->print_z;
f->overlap = 0;
}

View File

@@ -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");
}

View File

@@ -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;