mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-22 18:32:16 +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:
@@ -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;
|
||||
@@ -429,7 +430,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);
|
||||
@@ -463,11 +464,15 @@ 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;
|
||||
const Point3 &first_p3 = path.polyline.first_point();
|
||||
const Point3 &last_p3 = path.polyline.last_point();
|
||||
Point first_p = Point(first_p3.x(), first_p3.y());
|
||||
Point last_p = Point(last_p3.x(), last_p3.y());
|
||||
++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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,11 +660,13 @@ 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; }
|
||||
const Point3 &p3 = path_one.polyline.points[pt_idx];
|
||||
if (lines_two.distance_from_lines<false>(Point(p3.x(), p3.y())) < 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; }
|
||||
const Point3 &p3 = path_two.polyline.points[pt_idx];
|
||||
if (lines_one.distance_from_lines<false>(Point(p3.x(), p3.y())) < limit_distance) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1013,7 +1020,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 ||
|
||||
@@ -1025,7 +1032,8 @@ 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];
|
||||
const Point3 &p3 = overhang_region.front().polyline.points[i];
|
||||
Point p = Point(p3.x(), p3.y());
|
||||
if (double d = lower_layer_aabb_tree.distance_from_lines<true>(p) < min_dist) {
|
||||
min_dist = d;
|
||||
min_dist_idx = i;
|
||||
|
||||
Reference in New Issue
Block a user