feat: Add Z Anti-Aliasing (ZAA) contouring support (updated) (#12736)
This PR supersedes #12225, which originally proposed this feature but appears inactive. The feature originated from work I developed earlier in [BambuStudio-ZAA](https://github.com/adob/BambuStudio-ZAA), a private fork of Bambu Studio Compared to #12225, I updated the implementation for current upstream and fixed the following issues: - fixed broken tests - removed references to nonplanar directory Reviewers may want to compare against #12225 for earlier discussion/context. ## Summary Port of **Z Anti-Aliasing (ZAA)** from [BambuStudio-ZAA](https://github.com/adob/BambuStudio-ZAA) to OrcaSlicer. ZAA eliminates visible stair-stepping on curved and sloped top surfaces by raycasting each extrusion point against the original 3D mesh and micro-adjusting its Z height to follow the actual surface geometry. The result is visibly smoother domes, chamfers, and shallow slopes — without post-processing. ## How It Works 1. The slicer runs normally, then a **posContouring** step processes each layer 2. `ContourZ.cpp` raycasts every extrusion point vertically against the source mesh 3. Each point's Z is adjusted to the mesh intersection, converting flat `Polyline` paths into `Polyline3` paths with per-point Z coordinates 4. The G-code writer emits the adjusted Z values, so the printer follows the true surface ## Configuration Five new settings under **Print Settings > Quality**: | Setting | Type | Default | Description | |---------|------|---------|-------------| | `zaa_enabled` | bool | off | Master enable/disable switch | | `zaa_min_z` | float | 0.06 mm | Minimum Z layer height; controls slicing plane offset | | `zaa_minimize_perimeter_height` | float | 35° | Reduce perimeter heights on slopes below this angle (0 = disabled) | | `zaa_dont_alternate_fill_direction` | bool | off | Keep fill direction consistent instead of alternating | | `zaa_region_disable` | bool | off | Disable ZAA for a specific print region/material | ## Key Changes - **Core algorithm**: New `src/libslic3r/ContourZ.cpp` (~330 lines) — raycasting engine - **3D geometry**: `Point3`, `Line3`, `Polyline3`, `MultiPoint3` extend existing 2D types - **Arc fitting**: Templated to work with both 2D and 3D geometry - **Pipeline**: `ExtrusionPath::polyline` changed from `Polyline` to `Polyline3`; new `posContouring` step in `PrintObject.cpp` - **G-code**: `GCode.cpp` writes per-point Z when `path.z_contoured` is set - **UI**: ZAA settings exposed in Print Settings > Quality panel - **Documentation**: `docs/ZAA.md` with usage and implementation details 57 files changed, ~1800 insertions, ~200 deletions. ## Test Plan - [ ] Load a model with curved top surfaces (sphere, dome, chamfered box) - [ ] Enable **Z contouring** in Print Settings > Quality - [ ] Slice and verify G-code has varying Z values within contoured layers - [ ] Build on macOS (verified), test on Linux and Windows
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Config.hpp"
|
||||
#include "GCodeWriter.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "libslic3r.h"
|
||||
@@ -22,10 +23,12 @@
|
||||
#include "Time.hpp"
|
||||
#include "GCode/ExtrusionProcessor.hpp"
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
@@ -5521,12 +5524,12 @@ void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
|
||||
void GCode::set_origin(const Vec2d &pointf)
|
||||
{
|
||||
// if origin increases (goes towards right), last_pos decreases because it goes towards left
|
||||
const Point translate(
|
||||
const Point3 translate(
|
||||
scale_(m_origin(0) - pointf(0)),
|
||||
scale_(m_origin(1) - pointf(1))
|
||||
);
|
||||
m_last_pos += translate;
|
||||
m_wipe.path.translate(translate);
|
||||
m_wipe.path.translate(translate.to_point());
|
||||
m_origin = pointf;
|
||||
}
|
||||
|
||||
@@ -5603,11 +5606,15 @@ static std::unique_ptr<EdgeGrid::Grid> calculate_layer_edge_grid(const Layer& la
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters, const Point* start_point)
|
||||
std::string GCode::extrude_loop(const ExtrusionLoop& loop_ref,
|
||||
const std::string& description,
|
||||
double speed,
|
||||
const ExtrusionEntitiesPtr& region_perimeters,
|
||||
const Point* start_point)
|
||||
{
|
||||
|
||||
// get a copy; don't modify the orientation of the original loop object otherwise
|
||||
// next copies (if any) would not detect the correct orientation
|
||||
ExtrusionLoop loop = loop_ref;
|
||||
|
||||
bool is_hole = (loop.loop_role() & elrHole) == elrHole;
|
||||
|
||||
@@ -5682,13 +5689,14 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
const double nozzle_diam = nozzle_diameter;
|
||||
|
||||
// note: previous & next are inverted to extrude "in the opposite direction, and we are "rewinding"
|
||||
Point previous_point = paths.front().polyline.points[1];
|
||||
Point current_point = paths.front().polyline.points.front();
|
||||
Point next_point = paths.back().polyline.points.back();
|
||||
Point previous_point = Point(paths.front().polyline.points[1].x(), paths.front().polyline.points[1].y());
|
||||
Point current_point = Point(paths.front().polyline.points.front().x(), paths.front().polyline.points.front().y());
|
||||
Point next_point = Point(paths.back().polyline.points.back().x(), paths.back().polyline.points.back().y());
|
||||
|
||||
// can happen if seam_gap is null
|
||||
if (next_point == current_point) {
|
||||
next_point = paths.back().polyline.points[paths.back().polyline.points.size() - 2];
|
||||
const Point3 &p3 = paths.back().polyline.points[paths.back().polyline.points.size() - 2];
|
||||
next_point = Point(p3.x(), p3.y());
|
||||
}
|
||||
|
||||
Point a = next_point; // second point
|
||||
@@ -5735,7 +5743,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
// inside the model
|
||||
if(discoveredTouchingLines > 1){
|
||||
// use extrude instead of travel_to_xy to trigger the unretract
|
||||
ExtrusionPath fake_path_wipe(Polyline{pt, current_point}, paths.front());
|
||||
ExtrusionPath fake_path_wipe(Polyline3(Points3{Point3(pt), Point3(current_point)}), paths.front());
|
||||
fake_path_wipe.set_force_no_extrusion(true);
|
||||
fake_path_wipe.mm3_per_mm = 0;
|
||||
//fake_path_wipe.set_extrusion_role(erExternalPerimeter);
|
||||
@@ -5771,7 +5779,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
if (!enable_seam_slope) {
|
||||
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
|
||||
gcode += this->_extrude(*path, description, speed_for_path(*path));
|
||||
// Orca: Adaptive PA - dont adapt PA after the first pultipath extrusion is completed
|
||||
// Orca: Adaptive PA - dont adapt PA after the first multipath extrusion is completed
|
||||
// as we have already set the PA value to the average flow over the totality of the path
|
||||
// in the first extrude move
|
||||
// TODO: testing is needed with slope seams and adaptive PA.
|
||||
@@ -5829,10 +5837,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
for (ExtrusionPath &path : paths) {
|
||||
//BBS: Don't need to save duplicated point into wipe path
|
||||
if (!m_wipe.path.empty() && !path.empty() &&
|
||||
m_wipe.path.last_point() == path.first_point())
|
||||
m_wipe.path.append(path.polyline.points.begin() + 1, path.polyline.points.end());
|
||||
else
|
||||
m_wipe.path.append(path.polyline); // TODO: don't limit wipe to last path
|
||||
m_wipe.path.last_point() == Point(path.first_point().x(), path.first_point().y())) {
|
||||
// Convert Points3 to Points
|
||||
for (auto it = path.polyline.points.begin() + 1; it != path.polyline.points.end(); ++it)
|
||||
m_wipe.path.append(Point(it->x(), it->y()));
|
||||
} else
|
||||
m_wipe.path.append(path.polyline.to_polyline()); // TODO: don't limit wipe to last path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5842,8 +5852,10 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
// the side depends on the original winding order of the polygon (inwards for contours, outwards for holes)
|
||||
//FIXME improve the algorithm in case the loop is tiny.
|
||||
//FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query).
|
||||
Point a = paths.front().polyline.points[1]; // second point
|
||||
Point b = *(paths.back().polyline.points.end()-3); // second to last point
|
||||
const Point3 &a3 = paths.front().polyline.points[1]; // second point
|
||||
Point a = Point(a3.x(), a3.y());
|
||||
const Point3 &b3 = *(paths.back().polyline.points.end()-3); // second to last point
|
||||
Point b = Point(b3.x(), b3.y());
|
||||
if (is_hole == loop.is_counter_clockwise()) {
|
||||
// swap points
|
||||
Point c = a; a = b; b = c;
|
||||
@@ -5857,8 +5869,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
// create the destination point along the first segment and rotate it
|
||||
// we make sure we don't exceed the segment length because we don't know
|
||||
// the rotation of the second segment so we might cross the object boundary
|
||||
Vec2d p1 = paths.front().polyline.points.front().cast<double>();
|
||||
Vec2d p2 = paths.front().polyline.points[1].cast<double>();
|
||||
Vec2d p1 = paths.front().polyline.points.front().cast<double>().head<2>();
|
||||
Vec2d p2 = paths.front().polyline.points[1].cast<double>().head<2>();
|
||||
Vec2d v = p2 - p1;
|
||||
double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter));
|
||||
double l2 = v.squaredNorm();
|
||||
@@ -5870,19 +5882,20 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
||||
if (nd * nd < l2)
|
||||
pt = (p1 + threshold * v * (nd / sqrt(l2))).cast<coord_t>();
|
||||
//Point pt = ((nd * nd >= l2) ? (p1+v*0.4): (p1 + 0.2 * v * (nd / sqrt(l2)))).cast<coord_t>();
|
||||
pt.rotate(angle, paths.front().polyline.points.front());
|
||||
const Point3 ¢er3 = paths.front().polyline.points.front();
|
||||
pt.rotate(angle, Point(center3.x(), center3.y()));
|
||||
// generate the travel move
|
||||
gcode += m_writer.extrude_to_xy(this->point_to_gcode(pt), 0,"move inwards before travel",true);
|
||||
gcode += m_writer.extrude_to_xy(this->point_to_gcode(pt), 0, "move inwards before travel", true);
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string description, double speed)
|
||||
std::string GCode::extrude_multi_path(const ExtrusionMultiPath& multipath, const std::string& description, double speed)
|
||||
{
|
||||
// extrude along the path
|
||||
std::string gcode;
|
||||
|
||||
|
||||
//Orca: calculate multipath average mm3_per_mm value over the length of the path.
|
||||
//This is used for adaptive PA
|
||||
m_multi_flow_segment_path_pa_set = false; // always emit PA on the first path of the multi-path
|
||||
@@ -5899,8 +5912,8 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string
|
||||
if (total_multipath_length > 0.0)
|
||||
m_multi_flow_segment_path_average_mm3_per_mm = weighted_sum_mm3_per_mm / total_multipath_length;
|
||||
// Orca: end of multipath average mm3_per_mm value calculation
|
||||
|
||||
for (ExtrusionPath path : multipath.paths){
|
||||
|
||||
for (const ExtrusionPath &path : multipath.paths){
|
||||
gcode += this->_extrude(path, description, speed);
|
||||
// Orca: Adaptive PA - dont adapt PA after the first pultipath extrusion is completed
|
||||
// as we have already set the PA value to the average flow over the totality of the path
|
||||
@@ -5911,13 +5924,15 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string
|
||||
// BBS
|
||||
if (m_wipe.enable && FILAMENT_CONFIG(wipe)) {
|
||||
m_wipe.path = Polyline();
|
||||
for (ExtrusionPath &path : multipath.paths) {
|
||||
for (const ExtrusionPath &path : multipath.paths) {
|
||||
//BBS: Don't need to save duplicated point into wipe path
|
||||
if (!m_wipe.path.empty() && !path.empty() &&
|
||||
m_wipe.path.last_point() == path.first_point())
|
||||
m_wipe.path.append(path.polyline.points.begin() + 1, path.polyline.points.end());
|
||||
else
|
||||
m_wipe.path.append(path.polyline); // TODO: don't limit wipe to last path
|
||||
m_wipe.path.last_point() == Point(path.first_point().x(), path.first_point().y())) {
|
||||
// Convert Points3 to Points
|
||||
for (auto it = path.polyline.points.begin() + 1; it != path.polyline.points.end(); ++it)
|
||||
m_wipe.path.append(Point(it->x(), it->y()));
|
||||
} else
|
||||
m_wipe.path.append(path.polyline.to_polyline()); // TODO: don't limit wipe to last path
|
||||
}
|
||||
m_wipe.path.reverse();
|
||||
}
|
||||
@@ -5925,7 +5940,10 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string description, double speed, const ExtrusionEntitiesPtr& region_perimeters)
|
||||
std::string GCode::extrude_entity(const ExtrusionEntity& entity,
|
||||
const std::string& description,
|
||||
double speed,
|
||||
const ExtrusionEntitiesPtr& region_perimeters)
|
||||
{
|
||||
if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity))
|
||||
return this->extrude_path(*path, description, speed);
|
||||
@@ -5938,7 +5956,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string GCode::extrude_path(ExtrusionPath path, std::string description, double speed)
|
||||
std::string GCode::extrude_path(const ExtrusionPath& path, const std::string& description, double speed)
|
||||
{
|
||||
// Orca: Reset average multipath flow as this is a single line, single extrude volumetric speed path
|
||||
m_multi_flow_segment_path_pa_set = false;
|
||||
@@ -5946,17 +5964,17 @@ std::string GCode::extrude_path(ExtrusionPath path, std::string description, dou
|
||||
// description += ExtrusionEntity::role_to_string(path.role());
|
||||
std::string gcode = this->_extrude(path, description, speed);
|
||||
if (m_wipe.enable && FILAMENT_CONFIG(wipe)) {
|
||||
m_wipe.path = path.polyline;
|
||||
m_wipe.path = path.polyline.to_polyline();
|
||||
if (is_tree(this->config().support_type) && (path.role() == erSupportMaterial || path.role() == erSupportMaterialInterface || path.role() == erSupportTransition)) {
|
||||
if ((m_wipe.path.first_point() - m_wipe.path.last_point()).cast<double>().norm() > scale_(0.2)) {
|
||||
double min_dist = scale_(0.2);
|
||||
int i = 0;
|
||||
for (; i < path.polyline.points.size(); i++) {
|
||||
double dist = (path.polyline.points[i] - path.last_point()).cast<double>().norm();
|
||||
double dist = (path.polyline.points[i] - path.last_point3()).cast<double>().norm();
|
||||
if (dist < min_dist) min_dist = dist;
|
||||
if (min_dist < scale_(0.2) && dist > min_dist) break;
|
||||
}
|
||||
m_wipe.path = Polyline(Points(path.polyline.points.begin() + i - 1, path.polyline.points.end()));
|
||||
m_wipe.path = Polyline3(Points3(path.polyline.points.begin() + i - 1, path.polyline.points.end())).to_polyline();
|
||||
}
|
||||
} else
|
||||
m_wipe.path.reverse();
|
||||
@@ -5999,11 +6017,11 @@ std::string GCode::extrude_infill(const Print &print, const std::vector<ObjectBy
|
||||
extrusions.emplace_back(ee);
|
||||
if (! extrusions.empty()) {
|
||||
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||
chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
|
||||
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
|
||||
for (const ExtrusionEntity *fill : extrusions) {
|
||||
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
|
||||
if (eec) {
|
||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities)
|
||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos.to_point()).entities)
|
||||
gcode += this->extrude_entity(*ee, extrusion_name);
|
||||
} else
|
||||
gcode += this->extrude_entity(*fill, extrusion_name);
|
||||
@@ -6034,7 +6052,7 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
|
||||
if (extrusions.empty())
|
||||
return gcode;
|
||||
|
||||
chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
|
||||
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
|
||||
|
||||
const double support_speed = m_config.support_speed.value;
|
||||
const double support_interface_speed = m_config.get_abs_value("support_interface_speed");
|
||||
@@ -6200,14 +6218,20 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
// Move to first point of extrusion path
|
||||
// path is 2D. But in slope lift case, lift z is done in travel_to function.
|
||||
// Add m_need_change_layer_lift_z when change_layer in case of no lift if m_last_pos is equal to path.first_point() by chance
|
||||
if (!m_last_pos_defined || m_last_pos != path.first_point() || m_need_change_layer_lift_z || slope_need_z_travel) {
|
||||
Point first_point = path.first_point();
|
||||
if (!m_last_pos_defined || m_last_pos.to_point() != first_point || m_need_change_layer_lift_z || slope_need_z_travel) {
|
||||
const bool _last_pos_undefined = !m_last_pos_defined;
|
||||
gcode += this->travel_to(
|
||||
path.first_point(),
|
||||
path.role(),
|
||||
"move to first " + description + " point",
|
||||
sloped == nullptr ? DBL_MAX : get_sloped_z(sloped->slope_begin.z_ratio)
|
||||
);
|
||||
|
||||
double z = DBL_MAX;
|
||||
if (sloped != nullptr) {
|
||||
z = get_sloped_z(sloped->slope_begin.z_ratio);
|
||||
} else if (path.z_contoured && !path.polyline.lines().empty()) {
|
||||
z = unscale_(path.polyline.lines().begin()->a.z()) + m_nominal_z;
|
||||
}
|
||||
|
||||
gcode += this->travel_to(first_point, path.role(), "move to first " + description + " point", z);
|
||||
|
||||
m_need_change_layer_lift_z = false;
|
||||
// Orca: ensure Z matches planned layer height
|
||||
if (!slope_need_z_travel && (_last_pos_undefined || m_need_change_layer_lift_z)) {
|
||||
const std::string z_sync_comment = _last_pos_undefined ?
|
||||
@@ -6217,6 +6241,19 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
m_need_change_layer_lift_z = false;
|
||||
}
|
||||
|
||||
if (path.z_contoured && !path.polyline.lines().empty()) {
|
||||
double current_z = m_writer.get_position().z();
|
||||
double first_z = unscale_(path.polyline.lines().begin()->a.z()) + m_nominal_z;
|
||||
if (GCodeFormatter::quantize_xyzf(first_z) != GCodeFormatter::quantize_xyzf(current_z)) {
|
||||
gcode += m_writer.travel_to_z(first_z, "set Z for contouring", true);
|
||||
}
|
||||
}
|
||||
if (!path.z_contoured && sloped == nullptr) {
|
||||
double current_z = m_writer.get_position().z();
|
||||
if (GCodeFormatter::quantize_xyzf(current_z) != GCodeFormatter::quantize_xyzf(m_nominal_z)) {
|
||||
gcode += this->writer().travel_to_z(m_nominal_z, "reset Z after contouring", true);
|
||||
}
|
||||
}
|
||||
|
||||
// if needed, write the gcode_label_objects_end then gcode_label_objects_start
|
||||
// should be already done by travel_to, but just in case
|
||||
@@ -6803,10 +6840,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
}
|
||||
// BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode or we are doing sloped extrusion
|
||||
// Attention: G2 and G3 is not supported in spiral_mode mode
|
||||
if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr) {
|
||||
if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr || path.z_contoured) {
|
||||
double path_length = 0.;
|
||||
double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR;
|
||||
for (const Line& line : path.polyline.lines()) {
|
||||
double saved_z = m_writer.get_position().z();
|
||||
|
||||
for (const Line3& line : path.polyline.lines()) {
|
||||
std::string tempDescription = description;
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
if (line_length < EPSILON)
|
||||
@@ -6821,16 +6860,35 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
|
||||
}
|
||||
}
|
||||
if (sloped == nullptr) {
|
||||
if (path.z_contoured) {
|
||||
// ZAA: Z anti-aliased extrusion with variable Z per point
|
||||
Vec2d dest2d = this->point_to_gcode(line.b.to_point());
|
||||
coordf_t z_diff = unscale_(line.b.z());
|
||||
|
||||
double extrusion_ratio = 1;
|
||||
if (path.role() != erIroning) {
|
||||
extrusion_ratio = (path.height + z_diff) / path.height;
|
||||
}
|
||||
|
||||
double e = dE * extrusion_ratio;
|
||||
|
||||
double z = m_nominal_z + z_diff;
|
||||
if (z < 0.1) {
|
||||
throw RuntimeError("GCode: very low z");
|
||||
}
|
||||
gcode += m_writer.extrude_to_xyz(Vec3d(dest2d.x(), dest2d.y(), z), e,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
|
||||
} else if (sloped == nullptr) {
|
||||
// Normal extrusion
|
||||
gcode += m_writer.extrude_to_xy(
|
||||
this->point_to_gcode(line.b),
|
||||
this->point_to_gcode(line.b.to_point()),
|
||||
dE,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion());
|
||||
} else {
|
||||
// Sloped extrusion
|
||||
const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length);
|
||||
Vec2d dest2d = this->point_to_gcode(line.b);
|
||||
Vec2d dest2d = this->point_to_gcode(line.b.to_point());
|
||||
Vec3d dest3d(dest2d(0), dest2d(1), get_sloped_z(z_ratio));
|
||||
gcode += m_writer.extrude_to_xyz(
|
||||
dest3d,
|
||||
@@ -6849,7 +6907,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
size_t end_index = fitting_result[fitting_index].end_point_index;
|
||||
for (size_t point_index = start_index + 1; point_index < end_index + 1; point_index++) {
|
||||
tempDescription = description;
|
||||
const Line line = Line(path.polyline.points[point_index - 1], path.polyline.points[point_index]);
|
||||
const Line line = Line(path.polyline.points[point_index - 1].to_point(), path.polyline.points[point_index].to_point());
|
||||
const double line_length = line.length() * SCALING_FACTOR;
|
||||
if (line_length < EPSILON)
|
||||
continue;
|
||||
@@ -6907,14 +6965,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
double total_length = 0;
|
||||
if (sloped != nullptr) {
|
||||
// Calculate total extrusion length
|
||||
Points p;
|
||||
Points3 p;
|
||||
p.reserve(new_points.size());
|
||||
std::transform(new_points.begin(), new_points.end(), std::back_inserter(p), [](const ProcessedPoint& pp) { return pp.p; });
|
||||
Polyline l(p);
|
||||
Polyline3 l(p);
|
||||
total_length = l.length() * SCALING_FACTOR;
|
||||
}
|
||||
gcode += m_writer.set_speed(last_set_speed, "", comment);
|
||||
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
|
||||
Vec3d prev = this->point_to_gcode_quantized(new_points[0].p);
|
||||
bool pre_fan_enabled = false;
|
||||
bool cur_fan_enabled = false;
|
||||
if( m_enable_cooling_markers && enable_overhang_bridge_fan)
|
||||
@@ -6928,7 +6986,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
std::string tempDescription = description;
|
||||
const ProcessedPoint &processed_point = new_points[i];
|
||||
const ProcessedPoint &pre_processed_point = new_points[i-1];
|
||||
Vec2d p = this->point_to_gcode_quantized(processed_point.p);
|
||||
Vec3d p = this->point_to_gcode_quantized(processed_point.p);
|
||||
if (m_enable_cooling_markers) {
|
||||
if (enable_overhang_bridge_fan) {
|
||||
cur_fan_enabled = check_overhang_fan(processed_point.overlap, path.role());
|
||||
@@ -7010,9 +7068,26 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length);
|
||||
}
|
||||
}
|
||||
if (sloped == nullptr) {
|
||||
if (path.z_contoured) {
|
||||
Vec2d dest2d = p.head<2>();
|
||||
coordf_t z_diff = unscale_(processed_point.p.z());
|
||||
|
||||
double extrusion_ratio = 1;
|
||||
if (path.role() != erIroning) {
|
||||
extrusion_ratio = (path.height + z_diff) / path.height;
|
||||
}
|
||||
|
||||
double e = dE * extrusion_ratio;
|
||||
|
||||
double z = m_nominal_z + z_diff;
|
||||
if (z < 0.1) {
|
||||
throw RuntimeError("GCode: very low z");
|
||||
}
|
||||
gcode += m_writer.extrude_to_xyz(Vec3d(dest2d.x(), dest2d.y(), z), e,
|
||||
GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
} else if (sloped == nullptr) {
|
||||
// Normal extrusion
|
||||
gcode += m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
gcode += m_writer.extrude_to_xy(p.head<2>(), dE, GCodeWriter::full_gcode_comment ? tempDescription : "");
|
||||
} else {
|
||||
// Sloped extrusion
|
||||
const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length);
|
||||
@@ -7893,6 +7968,13 @@ Vec2d GCode::point_to_gcode(const Point &point) const
|
||||
return unscale(point) + m_origin - extruder_offset;
|
||||
}
|
||||
|
||||
Vec3d GCode::point_to_gcode(const Point3& point) const
|
||||
{
|
||||
Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset);
|
||||
Vec2d xy = unscale(point.to_point()) + m_origin - extruder_offset;
|
||||
return Vec3d(xy.x(), xy.y(), unscale_(point.z()));
|
||||
}
|
||||
|
||||
// convert a model-space scaled point into G-code coordinates
|
||||
Point GCode::gcode_to_point(const Vec2d &point) const
|
||||
{
|
||||
@@ -7910,6 +7992,11 @@ Vec2d GCode::point_to_gcode_quantized(const Point& point) const
|
||||
return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) };
|
||||
}
|
||||
|
||||
Vec3d GCode::point_to_gcode_quantized(const Point3& point) const
|
||||
{
|
||||
Vec3d p = this->point_to_gcode(point);
|
||||
return {GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()), GCodeFormatter::quantize_xyzf(p.z())};
|
||||
}
|
||||
|
||||
// Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed
|
||||
// during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
|
||||
|
||||
Reference in New Issue
Block a user