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 2317ba7e28
commit 6250fab6a4
57 changed files with 1817 additions and 204 deletions

1
.gitignore vendored
View File

@@ -43,3 +43,4 @@ test.js
/.cache/
.clangd
internal_docs/
resources/nonplanar/

41
docs/ZAA.md Normal file
View File

@@ -0,0 +1,41 @@
# Z Anti-Aliasing (ZAA) — Z Contouring
ZAA eliminates stair-stepping on curved and sloped top surfaces by adjusting the Z height of each extrusion point to follow the actual 3D model surface.
Instead of printing flat horizontal layers, ZAA raycasts each point of the toolpath against the original mesh and micro-adjusts its Z coordinate to match the true surface geometry. The result is visibly smoother surfaces on domes, chamfers, and shallow slopes — without post-processing.
This is a port of the ZAA implementation from [BambuStudio-ZAA](https://github.com/adob/BambuStudio-ZAA) by adob.
## Configuration
ZAA adds five 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; also controls the 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 per layer |
| `zaa_region_disable` | bool | off | Disable ZAA for a specific print region/material |
## How It Works
1. The slicer slices normally, then runs a **posContouring** step on 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 that carry per-point Z coordinates.
4. The G-code writer emits the adjusted Z values, so the printer follows the true surface.
## Key Implementation Details
- **Core algorithm**: `src/libslic3r/ContourZ.cpp` (~330 lines)
- **3D geometry**: `Point3`, `Line3`, `Polyline3`, `MultiPoint3` extend the existing 2D types
- **Pipeline step**: `posContouring` in `PrintObject.cpp`, runs after perimeter/infill generation
- **G-code output**: `GCode.cpp` writes per-point Z when `path.z_contoured` is set
- **Arc fitting**: Templated to work with both 2D and 3D geometry
- **ExtrusionPath change**: `polyline` field changed from `Polyline` to `Polyline3`
## Testing
1. Load a model with curved top surfaces (spheres, domes, chamfered edges)
2. Enable **Z contouring** in Print Settings > Quality
3. Slice and inspect the G-code — Z values should vary within each layer on contoured surfaces

View File

@@ -1,4 +1,5 @@
#include "ArcFitter.hpp"
#include "Point.hpp"
#include "Polyline.hpp"
#include <cmath>
@@ -6,7 +7,17 @@
namespace Slic3r {
void ArcFitter::do_arc_fitting(const Points& points, std::vector<PathFittingData>& result, double tolerance)
// Helper functions to dispatch to the correct douglas_peucker implementation
static inline Points douglas_peucker_helper(const Points &points, double tolerance) {
return MultiPoint::_douglas_peucker(points, tolerance);
}
static inline Points3 douglas_peucker_helper(const Points3 &points, double tolerance) {
return MultiPoint3::_douglas_peucker(points, tolerance);
}
template <typename POINTS>
static void do_arc_fitting_tmpl(const POINTS& points, std::vector<PathFittingData>& result, double tolerance)
{
#ifdef DEBUG_ARC_FITTING
static int irun = 0;
@@ -39,7 +50,7 @@ void ArcFitter::do_arc_fitting(const Points& points, std::vector<PathFittingData
size_t back_index = 0;
ArcSegment last_arc;
bool can_fit = false;
Points current_segment;
POINTS current_segment;
current_segment.reserve(points.size());
ArcSegment target_arc;
for (size_t i = 0; i < points.size(); i++) {
@@ -49,7 +60,7 @@ void ArcFitter::do_arc_fitting(const Points& points, std::vector<PathFittingData
if (back_index - front_index < 2)
continue;
can_fit = ArcSegment::try_create_arc(current_segment, target_arc, Polyline(current_segment).length(),
can_fit = ArcSegment::try_create_arc(current_segment, target_arc, to_polyline(current_segment).length(),
DEFAULT_SCALED_MAX_RADIUS,
tolerance,
DEFAULT_ARC_LENGTH_PERCENT_TOLERANCE);
@@ -57,20 +68,20 @@ void ArcFitter::do_arc_fitting(const Points& points, std::vector<PathFittingData
//BBS: can be fit as arc, then save arc data temperarily
last_arc = target_arc;
if (back_index == points.size() - 1) {
result.emplace_back(std::move(PathFittingData{ front_index,
result.emplace_back(PathFittingData{ front_index,
back_index,
last_arc.direction == ArcDirection::Arc_Dir_CCW ? EMovePathType::Arc_move_ccw : EMovePathType::Arc_move_cw,
last_arc }));
last_arc });
front_index = back_index;
}
} else {
if (back_index - front_index > 2) {
//BBS: althought current point_stack can't be fit as arc,
//but previous must can be fit if removing the top in stack, so save last arc
result.emplace_back(std::move(PathFittingData{ front_index,
result.emplace_back(PathFittingData{ front_index,
back_index - 1,
last_arc.direction == ArcDirection::Arc_Dir_CCW ? EMovePathType::Arc_move_ccw : EMovePathType::Arc_move_cw,
last_arc }));
last_arc });
} else {
//BBS: save the first segment as line move when 3 point-line can't be fit as arc move
if (result.empty() || result.back().path_type != EMovePathType::Linear_move)
@@ -94,7 +105,18 @@ void ArcFitter::do_arc_fitting(const Points& points, std::vector<PathFittingData
result.shrink_to_fit();
}
void ArcFitter::do_arc_fitting_and_simplify(Points& points, std::vector<PathFittingData>& result, double tolerance)
void ArcFitter::do_arc_fitting(const Points &points, std::vector<PathFittingData>& result, double tolerance)
{
do_arc_fitting_tmpl(points, result, tolerance);
}
void ArcFitter::do_arc_fitting(const Points3 &points, std::vector<PathFittingData>& result, double tolerance)
{
do_arc_fitting_tmpl(points, result, tolerance);
}
template <typename POINTS>
static void do_arc_fitting_and_simplify_tmpl(POINTS &points, std::vector<PathFittingData>& result, double tolerance)
{
//BBS: 1 do arc fit first
if (abs(tolerance) > SCALED_EPSILON)
@@ -106,12 +128,12 @@ void ArcFitter::do_arc_fitting_and_simplify(Points& points, std::vector<PathFitt
//for arc part, only need to keep start and end point
if (result.size() == 1 && result[0].path_type == EMovePathType::Linear_move) {
//BBS: all are straight segment, directly use DP simplify
points = MultiPoint::_douglas_peucker(points, tolerance);
points = douglas_peucker_helper(points, tolerance);
result[0].end_point_index = points.size() - 1;
return;
} else {
//BBS: has both arc part and straight part, we should spilit the straight part out and do DP simplify
Points simplified_points;
POINTS simplified_points;
simplified_points.reserve(points.size());
simplified_points.push_back(points[0]);
std::vector<size_t> reduce_count(result.size(), 0);
@@ -124,11 +146,11 @@ void ArcFitter::do_arc_fitting_and_simplify(Points& points, std::vector<PathFitt
//For arc part, theoretically, we only need to keep the start and end point, and
//delete all other point. But when considering wipe operation, we must keep the original
//point data and shouldn't reduce too much by only saving start and end point.
Points straight_or_arc_part;
POINTS straight_or_arc_part;
straight_or_arc_part.reserve(end_index - start_index + 1);
for (size_t j = start_index; j <= end_index; j++)
straight_or_arc_part.push_back(points[j]);
straight_or_arc_part = MultiPoint::_douglas_peucker(straight_or_arc_part, tolerance);
straight_or_arc_part = douglas_peucker_helper(straight_or_arc_part, tolerance);
//BBS: how many point has been reduced
reduce_count[i] = end_index - start_index + 1 - straight_or_arc_part.size();
//BBS: save the simplified result
@@ -150,4 +172,14 @@ void ArcFitter::do_arc_fitting_and_simplify(Points& points, std::vector<PathFitt
}
}
void ArcFitter::do_arc_fitting_and_simplify(Points& points, std::vector<PathFittingData>& result, double tolerance)
{
do_arc_fitting_and_simplify_tmpl(points, result, tolerance);
}
void ArcFitter::do_arc_fitting_and_simplify(Points3& points, std::vector<PathFittingData>& result, double tolerance)
{
do_arc_fitting_and_simplify_tmpl(points, result, tolerance);
}
}

View File

@@ -42,9 +42,11 @@ class ArcFitter {
public:
//BBS: this function is used to check the point list and return which part can fit as arc, which part should be line
static void do_arc_fitting(const Points& points, std::vector<PathFittingData> &result, double tolerance);
static void do_arc_fitting(const Points3& points, std::vector<PathFittingData> &result, double tolerance);
//BBS: this function is used to check the point list and return which part can fit as arc, which part should be line.
//By the way, it also use DP simplify to reduce point of straight part and only keep the start and end point of arc.
static void do_arc_fitting_and_simplify(Points& points, std::vector<PathFittingData>& result, double tolerance);
static void do_arc_fitting_and_simplify(Points3& points, std::vector<PathFittingData>& result, double tolerance);
};
}

View File

@@ -236,7 +236,18 @@ class BoundingBox3 : public BoundingBox3Base<Vec3crd>
public:
BoundingBox3() : BoundingBox3Base<Vec3crd>() {}
BoundingBox3(const Vec3crd &pmin, const Vec3crd &pmax) : BoundingBox3Base<Vec3crd>(pmin, pmax) {}
BoundingBox3(const Points3& points) : BoundingBox3Base<Vec3crd>(points) {}
BoundingBox3(const Points3& points) : BoundingBox3Base<Vec3crd>() {
if (!points.empty()) {
this->min = points.front();
this->max = points.front();
for (const auto &p : points) {
this->min = this->min.cwiseMin(static_cast<const Vec3crd&>(p));
this->max = this->max.cwiseMax(static_cast<const Vec3crd&>(p));
}
this->defined = true;
}
}
BoundingBox3(const std::vector<Vec3crd>& points) : BoundingBox3Base<Vec3crd>(points) {}
};
class BoundingBoxf : public BoundingBoxBase<Vec2d>

View File

@@ -100,6 +100,7 @@ set(lisbslic3r_sources
CommonDefs.hpp
Config.cpp
Config.hpp
ContourZ.cpp
CustomGCode.cpp
CustomGCode.hpp
CutUtils.cpp

View File

@@ -3,6 +3,7 @@
#include <cmath>
#include <cassert>
#include "Geometry.hpp"
#include "Polygon.hpp"
//BBS: Refer to ArcWelderLib for the arc fitting functions
@@ -94,6 +95,12 @@ bool Circle::try_create_circle(const Points& points, const double max_radius, co
return found_circle;
}
bool Circle::try_create_circle(const Points3& points, const double max_radius, const double tolerance, Circle& new_circle)
{
return Circle::try_create_circle(to_points(points), max_radius, tolerance, new_circle);
}
double Circle::get_polar_radians(const Point& p1) const
{
double polar_radians = atan2(p1.y() - center.y(), p1.x() - center.x());
@@ -291,6 +298,31 @@ bool ArcSegment::try_create_arc(
return false;
}
bool ArcSegment::try_create_arc(
const Points3& points,
ArcSegment& target_arc,
double approximate_length,
double max_radius,
double tolerance,
double path_tolerance_percent)
{
Circle test_circle = (Circle)target_arc;
if (!Circle::try_create_circle(points, max_radius, tolerance, test_circle))
return false;
int mid_point_index = ((points.size() - 2) / 2) + 1;
ArcSegment test_arc;
if (!ArcSegment::try_create_arc(test_circle, points[0].to_point(), points[mid_point_index].to_point(), points[points.size() - 1].to_point(), test_arc, approximate_length, path_tolerance_percent))
return false;
if (ArcSegment::are_points_within_slice(test_arc, points))
{
target_arc = test_arc;
return true;
}
return false;
}
bool ArcSegment::try_create_arc(
const Circle& c,
const Point& start_point,
@@ -455,6 +487,87 @@ bool ArcSegment::are_points_within_slice(const ArcSegment& test_arc, const Point
return true;
}
bool ArcSegment::are_points_within_slice(const ArcSegment& test_arc, const Points3& points)
{
//BBS: Check all the points and see if they fit inside of the angles
double previous_polar = test_arc.polar_start_theta;
bool will_cross_zero = false;
bool crossed_zero = false;
const int point_count = points.size();
Vec2d start_norm(((double)test_arc.start_point.x() - (double)test_arc.center.x()) / test_arc.radius,
((double)test_arc.start_point.y() - (double)test_arc.center.y()) / test_arc.radius);
Vec2d end_norm(((double)test_arc.end_point.x() - (double)test_arc.center.x()) / test_arc.radius,
((double)test_arc.end_point.y() - (double)test_arc.center.y()) / test_arc.radius);
if (test_arc.direction == ArcDirection::Arc_Dir_CCW)
will_cross_zero = test_arc.polar_start_theta > test_arc.polar_end_theta;
else
will_cross_zero = test_arc.polar_start_theta < test_arc.polar_end_theta;
//BBS: check if point 1 to point 2 cross zero
double polar_test;
for (int index = point_count - 2; index < point_count; index++)
{
if (index < point_count - 1)
polar_test = test_arc.get_polar_radians(points[index].to_point());
else
polar_test = test_arc.polar_end_theta;
//BBS: First ensure the test point is within the arc
if (test_arc.direction == ArcDirection::Arc_Dir_CCW)
{
//BBS: Only check to see if we are within the arc if this isn't the endpoint
if (index < point_count - 1) {
if (will_cross_zero) {
if (!(polar_test > test_arc.polar_start_theta || polar_test < test_arc.polar_end_theta))
return false;
} else if (!(test_arc.polar_start_theta < polar_test && polar_test < test_arc.polar_end_theta))
return false;
}
//BBS: check the angles are increasing
if (previous_polar > polar_test) {
if (!will_cross_zero)
return false;
//BBS: Allow the angle to cross zero once
if (crossed_zero)
return false;
crossed_zero = true;
}
} else {
if (index < point_count - 1) {
if (will_cross_zero) {
if (!(polar_test < test_arc.polar_start_theta || polar_test > test_arc.polar_end_theta))
return false;
} else if (!(test_arc.polar_start_theta > polar_test && polar_test > test_arc.polar_end_theta))
return false;
}
//BBS: Now make sure the angles are decreasing
if (previous_polar < polar_test)
{
if (!will_cross_zero)
return false;
//BBS: Allow the angle to cross zero once
if (crossed_zero)
return false;
crossed_zero = true;
}
}
// BBS: check if the segment intersects either of the vector from the center of the circle to the endpoints of the arc
Line segmemt(points[index - 1].to_point(), points[index].to_point());
if ((index != 1 && ray_intersects_segment(test_arc.center, start_norm, segmemt)) ||
(index != point_count - 1 && ray_intersects_segment(test_arc.center, end_norm, segmemt)))
return false;
previous_polar = polar_test;
}
//BBS: Ensure that all arcs that cross zero
if (will_cross_zero != crossed_zero)
return false;
return true;
}
// BBS: this function is used to detect whether a ray cross the segment
bool ArcSegment::ray_intersects_segment(const Point &rayOrigin, const Vec2d &rayDirection, const Line& segment)
{

View File

@@ -28,6 +28,7 @@ public:
static bool try_create_circle(const Point &p1, const Point &p2, const Point &p3, const double max_radius, Circle& new_circle);
static bool try_create_circle(const Points& points, const double max_radius, const double tolerance, Circle& new_circle);
static bool try_create_circle(const Points3& points, const double max_radius, const double tolerance, Circle& new_circle);
double get_polar_radians(const Point& p1) const;
bool is_over_deviation(const Points& points, const double tolerance);
bool get_deviation_sum_squared(const Points& points, const double tolerance, double& sum_deviation);
@@ -111,8 +112,16 @@ public:
double max_radius = DEFAULT_SCALED_MAX_RADIUS,
double tolerance = DEFAULT_SCALED_RESOLUTION,
double path_tolerance_percent = DEFAULT_ARC_LENGTH_PERCENT_TOLERANCE);
static bool try_create_arc(
const Points3 &points,
ArcSegment& target_arc,
double approximate_length,
double max_radius = DEFAULT_SCALED_MAX_RADIUS,
double tolerance = DEFAULT_SCALED_RESOLUTION,
double path_tolerance_percent = DEFAULT_ARC_LENGTH_PERCENT_TOLERANCE);
static bool are_points_within_slice(const ArcSegment& test_arc, const Points &points);
static bool are_points_within_slice(const ArcSegment& test_arc, const Points3 &points);
// BBS: this function is used to detect whether a ray cross the segment
static bool ray_intersects_segment(const Point& rayOrigin, const Vec2d& rayDirection, const Line& segment);
// BBS: these three functions are used to calculate related arguments of arc in unscale_field.

View File

@@ -411,6 +411,19 @@ Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta
Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type)
{ assert(delta > 0); return to_polygons(clipper_union<ClipperLib::Paths>(raw_offset_polyline(ClipperUtils::SinglePathProvider(polyline.points), delta, joinType, miterLimit, end_type))); }
Slic3r::Polygons offset(const Slic3r::Polyline3 &polyline, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type)
{
assert(delta > 0);
return to_polygons(
clipper_union<ClipperLib::Paths>(
raw_offset_polyline(
ClipperUtils::SinglePathProvider(polyline.to_polyline().points),
delta,
joinType,
miterLimit,
end_type)));
}
Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType, double miterLimit, ClipperLib::EndType end_type)
{ assert(delta > 0); return to_polygons(clipper_union<ClipperLib::Paths>(raw_offset_polyline(ClipperUtils::PolylinesProvider(polylines), delta, joinType, miterLimit, end_type))); }

View File

@@ -334,6 +334,7 @@ Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, Clipp
// Wherever applicable, please use the expand() / shrink() variants instead, they convey their purpose better.
// Input polygons for negative offset shall be "normalized": There must be no overlap / intersections between the input polygons.
Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polyline3 &polyline, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = DefaultLineJoinType, double miterLimit = DefaultLineMiterLimit, ClipperLib::EndType end_type = DefaultEndType);
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = DefaultJoinType, double miterLimit = DefaultMiterLimit);
@@ -524,6 +525,8 @@ Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip);
Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip);
Slic3r::Polylines3 intersection_pl(const Slic3r::Polylines3 &subject, const Slic3r::Polygon &clip);
Slic3r::Polylines3 intersection_pl(const Slic3r::Polylines3 &subject, const Slic3r::ExPolygon &clip);
inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
{

333
src/libslic3r/ContourZ.cpp Normal file
View File

@@ -0,0 +1,333 @@
#include "Exception.hpp"
#include "ExtrusionEntity.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "Layer.hpp"
#include "Point.hpp"
#include "Print.hpp"
#include "SLA/IndexedMesh.hpp"
#include "libslic3r.h"
#include <cfloat>
#include <cmath>
#include <initializer_list>
#include <string>
namespace Slic3r {
static void contour_extrusion_entity(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntity *extr);
// static double lowest_z_within_distance(const Vec3d &normal, double dist) {
// const Vec3d p(0.0, 0.0, 0.0);
// Eigen::Vector3d n_unit = normal.normalized();
// Eigen::Vector3d z_hat(0.0, 0.0, 1.0);
// // Project the negative z-direction into the tangent plane
// Eigen::Vector3d v_dir = -z_hat + (z_hat.dot(n_unit)) * n_unit;
// double norm_v = v_dir.norm();
// if (norm_v == 0.0) {
// // Surface is horizontal, cannot go lower in z within tangent plane
// return p.z();
// }
// Eigen::Vector3d v = dist * v_dir / norm_v;
// Eigen::Vector3d q = p + v;
// return q.z();
// }
static double follow_slope_down(double angle_rad, double dist) {
return -dist * std::sin(angle_rad);
}
static double slope_from_normal(const Eigen::Vector3d& normal) {
// Ensure the normal is normalized
Eigen::Vector3d n = normal.normalized();
// Compute angle between normal and z-axis
double angle_rad = std::acos(std::abs(n.z())); // angle between normal and vertical
return angle_rad;
// calculate fall over dist
// double dist = 0.2;
// double z_dist = lowest_z(angle_rad, dist);
// printf("fall %f vs %f\n", z_dist, lowest_z_within_distance(normal, dist));
// double angle_deg = angle_rad * 180.0 / M_PI;
// return angle_deg;
}
// const int LINE = 180;
static bool contour_extrusion_path(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionPath &path) {
if (region->region().config().zaa_region_disable) {
return false;
}
if (path.role() != erTopSolidInfill && path.role() != erIroning && path.role() != erExternalPerimeter && path.role() != erPerimeter) {
return false;
}
Layer *layer = region->layer();
coordf_t mesh_z = layer->print_z + mesh.ground_level();
coordf_t min_z = layer->object()->config().zaa_min_z;
const Points3 &points = path.polyline.points;
double resolution_mm = 0.1;
coordf_t height = layer->height;
// std::cout << "LAYER " << (layer->id()+1) << std::endl;
// std::cout << "PRINT Z " << layer->print_z << std::endl;
// std::cout << "LAYER HEIGHT " << height << std::endl;
// std::cout << "EXTRUSION HEIGHT " << path.height << std::endl;
// std::cout << "EXTRUSION WIDTH " << path.width << std::endl;
// std::cout << "EXTRUSION ROLE: " << ExtrusionEntity::role_to_string(path.role()) << std::endl;
// std::cout << "FIRST POINT: " << path.polyline.first_point() << std::endl;
double minimize_perimeter_height_angle = region->region().config().zaa_minimize_perimeter_height;
Pointf3s contoured_points;
bool was_contoured = false;
// bool is_perimeter = path.role() == erExternalPerimeter || path.role() == erPerimeter || path.role() == erOverhangPerimeter;
for (Points3::const_iterator it = points.begin(); it != points.end()-1; ++it) {
Vec2d p1d(unscale_(it->x()), unscale_(it->y()));
Vec2d p2d(unscale_((it+1)->x()), unscale_((it+1)->y()));
Linef line(p1d, p2d);
double length_mm = line.length();
int num_segments = int(std::ceil(length_mm / resolution_mm));
Vec2d delta = line.vector();
for (int i = 0; i < num_segments+1; i++) {
Vec2d p = p1d + delta*i/num_segments;
coordf_t x = p.x();
coordf_t y = p.y();
sla::IndexedMesh::hit_result hit_up = mesh.query_ray_hit({x, y, mesh_z}, {0.0, 0.0, 1.0});
sla::IndexedMesh::hit_result hit_down = mesh.query_ray_hit({x, y, mesh_z}, {0.0, 0.0, -1.0});
double up = hit_up.distance();
double down = hit_down.distance();
double d = up < down ? up : -down;
const Vec3d &normal = (up < down ? hit_up : hit_down).normal();
double max_up = min_z;
double min_down = -(height - min_z);
double half_width = path.width / 2.0;
if (path.role() == erIroning) {
max_up = height;
min_down = -(height + 0.1);
}
double slope_rad = slope_from_normal(normal);
double slope_degrees = slope_rad * 180.0 / M_PI;
if (d > min_down && minimize_perimeter_height_angle > 0 && minimize_perimeter_height_angle < slope_degrees && path.role() == erExternalPerimeter) {
double adjustment = follow_slope_down(slope_rad, half_width);
if (adjustment > 0) {
throw RuntimeError("ContourZ: got positive adjustment");
}
d += adjustment;
if (d < min_down) {
d = min_down;
}
}
if (d > max_up + 0.03 || d < min_down) {
d = 0;
} else {
if (d > max_up) {
d = max_up;
}
}
if (path.role() == erExternalPerimeter && d > 0) {
// do not increase height of external perimeters as this may create an appearance of a seam
d = 0;
}
if (std::abs(d) > EPSILON) {
was_contoured = true;
}
Vec3d new_point = {p.x(), p.y(), d};
if (contoured_points.size() > 2) {
double dist = Linef3::distance_to_infinite_squared(
contoured_points[contoured_points.size() - 2],
contoured_points[contoured_points.size() - 1],
new_point);
if (dist < EPSILON) {
contoured_points[contoured_points.size() - 1] = new_point;
continue;
}
}
contoured_points.push_back(new_point);
}
}
if (!was_contoured) {
return false;
}
Polyline3 polyline;
for (const Vec3d &point : contoured_points) {
polyline.append(Point3(scale_(point.x()), scale_(point.y()), scale_(point.z())));
}
path.polyline = std::move(polyline);
path.z_contoured = true;
return true;
}
static void contour_extrusion_multipath(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionMultiPath &multipath)
{
for (ExtrusionPath &path : multipath.paths) {
contour_extrusion_path(region, mesh, path);
}
}
static void contour_extrusion_loop(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionLoop &loop)
{
for (ExtrusionPath &path : loop.paths) {
contour_extrusion_path(region, mesh, path);
}
}
static void contour_extrusion_entitiy_collection(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntityCollection &collection) {
for (ExtrusionEntity *entity : collection.entities) {
contour_extrusion_entity(region, mesh, entity);
}
}
static void contour_extrusion_entity(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntity *extr) {
const ExtrusionPathSloped *sloped = dynamic_cast<const ExtrusionPathSloped*>(extr);
if (sloped != nullptr) {
throw RuntimeError("ExtrusionPathSloped not implemented");
return;
}
ExtrusionMultiPath *multipath = dynamic_cast<ExtrusionMultiPath*>(extr);
if (multipath != nullptr) {
contour_extrusion_multipath(region, mesh, *multipath);
return;
}
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(extr);
if (path != nullptr) {
contour_extrusion_path(region, mesh, *path);
return;
}
ExtrusionLoop *loop = dynamic_cast<ExtrusionLoop*>(extr);
if (loop != nullptr) {
contour_extrusion_loop(region, mesh, *loop);
return;
}
const ExtrusionLoopSloped *loop_sloped = dynamic_cast<const ExtrusionLoopSloped*>(extr);
if (loop_sloped != nullptr) {
throw RuntimeError("ExtrusionLoopSloped not implemented");
return;
}
ExtrusionEntityCollection *collection = dynamic_cast<ExtrusionEntityCollection*>(extr);
if (collection != nullptr) {
contour_extrusion_entitiy_collection(region, mesh, *collection);
return;
}
throw RuntimeError("ContourZ: ExtrusionEntity type not implemented: " + std::string(typeid(*extr).name()));
return;
}
static void handle_extrusion_collection(LayerRegion *region, const sla::IndexedMesh &mesh, ExtrusionEntityCollection &collection, std::initializer_list<ExtrusionRole> roles) {
for (ExtrusionEntity *extr : collection.entities) {
// printf("handling extrusion collection %p %p\n", &collection, extr);
if (!contains(roles, extr->role())) {
continue;
}
contour_extrusion_entity(region, mesh, extr);
}
}
// static void find_point(ExtrusionPath &path, const std::string &path_info) {
// Points3 &points = path.polyline.points;
// size_t i = 0;
// for (Points3::const_iterator it = points.begin(); it != points.end()-1; ++it) {
// if (it->x() == -883971 && it->y() == 979001) {
// std::cout << "FOUND POINT " << ExtrusionEntity::role_to_string(path.role()) << " at path " << path_info << "[" + std::to_string(i) + "]" << std::endl;
// }
// i++;
// }
// }
// static void find_point(ExtrusionLoop &loop, const std::string &path_info) {
// size_t i = 0;
// for (ExtrusionPath &path : loop.paths) {
// find_point(path, path_info + "[" + std::to_string(i) + "]");
// i++;
// }
// }
// static void find_point(ExtrusionEntity &extr, const std::string &path);
// static void find_point(ExtrusionEntityCollection &collection, const std::string &path) {
// size_t i = 0;
// for (ExtrusionEntity *extr : collection.entities) {
// find_point(*extr, path + "[" + std::to_string(i) + "]");
// i++;
// }
// }
// static void find_point(ExtrusionEntity &extr, const std::string &path_info) {
// const ExtrusionPathSloped *sloped = dynamic_cast<const ExtrusionPathSloped*>(&extr);
// if (sloped != nullptr) {
// throw RuntimeError("ExtrusionPathSloped not implemented");
// return;
// }
// ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(&extr);
// if (path != nullptr) {
// find_point(*path, path_info + " as ExtrusionPath " + ExtrusionEntity::role_to_string(extr.role()));
// return;
// }
// ExtrusionLoop *loop = dynamic_cast<ExtrusionLoop*>(&extr);
// if (loop != nullptr) {
// find_point(*loop, path_info + " as ExtrusionLoop " + ExtrusionEntity::role_to_string(extr.role()));
// return;
// }
// const ExtrusionLoopSloped *loop_sloped = dynamic_cast<const ExtrusionLoopSloped*>(&extr);
// if (loop_sloped != nullptr) {
// throw RuntimeError("ExtrusionLoopSloped not implemented");
// return;
// }
// ExtrusionEntityCollection *collection = dynamic_cast<ExtrusionEntityCollection*>(&extr);
// if (collection != nullptr) {
// find_point(*collection, path_info + " as ExtrusionEntityCollection " + ExtrusionEntity::role_to_string(extr.role()));
// return;
// }
// throw RuntimeError("ContourZ: ExtrusionEntity type not implemented");
// return;
// }
void Layer::make_contour_z(const sla::IndexedMesh &mesh)
{
// printf("make_contour_z() called\n");
for (LayerRegion *region : this->regions()) {
// printf("processing layer region %p\n", region);
// find_point(region->fills, "fills");
// find_point(region->perimeters, "perimeters");
handle_extrusion_collection(region, mesh, region->fills, {erTopSolidInfill, erIroning, erExternalPerimeter, erMixed});
handle_extrusion_collection(region, mesh, region->perimeters, {erExternalPerimeter, erMixed});
}
}
} // namespace Slic3r

View File

@@ -17,12 +17,12 @@ static const double slope_inner_outer_wall_gap = 0.4;
void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const
{
this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection), retval);
this->_inflate_collection(intersection_pl(Polylines{ polyline.to_polyline() }, collection), retval);
}
void ExtrusionPath::subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const
{
this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection), retval);
this->_inflate_collection(diff_pl(Polylines{ this->polyline.to_polyline() }, collection), retval);
}
void ExtrusionPath::clip_end(double distance)
@@ -32,11 +32,17 @@ void ExtrusionPath::clip_end(double distance)
void ExtrusionPath::simplify(double tolerance)
{
if (this->z_contoured) {
return;
}
this->polyline.simplify(tolerance);
}
void ExtrusionPath::simplify_by_fitting_arc(double tolerance)
{
if (this->z_contoured) {
return;
}
this->polyline.simplify_by_fitting_arc(tolerance);
}
@@ -45,15 +51,23 @@ double ExtrusionPath::length() const
return this->polyline.length();
}
void ExtrusionPath::collect_points(Points &dst) const
{
dst.reserve(dst.size() + this->polyline.points.size());
for (const Point3 &point : this->polyline.points) {
dst.emplace_back(point.x(), point.y());
}
}
void ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
{
for (const Polyline &polyline : polylines)
collection->entities.emplace_back(new ExtrusionPath(polyline, *this));
collection->entities.emplace_back(new ExtrusionPath(Polyline3(polyline), *this));
}
void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
{
polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon));
polygons_append(out, offset(this->polyline.to_polyline(), float(scale_(this->width/2)) + scaled_epsilon));
}
void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
@@ -64,7 +78,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale
// SoftFever: TODO Mac trigger assersion errors
// assert(! bridge || this->width == this->height);
auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f);
polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
polygons_append(out, offset(this->polyline.to_polyline(), 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
}
void ExtrusionMultiPath::reverse()
@@ -116,9 +130,10 @@ Polyline ExtrusionMultiPath::as_polyline() const
len -= paths.size() - 1;
assert(len > 0);
out.points.reserve(len);
out.points.push_back(paths.front().polyline.points.front());
out.points.push_back(paths.front().polyline.points.front().to_point());
for (size_t i_path = 0; i_path < paths.size(); ++ i_path)
out.points.insert(out.points.end(), paths[i_path].polyline.points.begin() + 1, paths[i_path].polyline.points.end());
for (auto it = paths[i_path].polyline.points.begin() + 1; it != paths[i_path].polyline.points.end(); ++it)
out.points.push_back(it->to_point());
}
return out;
}
@@ -149,7 +164,9 @@ Polygon ExtrusionLoop::polygon() const
Polygon polygon;
for (const ExtrusionPath &path : this->paths) {
// for each polyline, append all points except the last one (because it coincides with the first one of the next polyline)
polygon.points.insert(polygon.points.end(), path.polyline.points.begin(), path.polyline.points.end()-1);
for (auto it = path.polyline.points.begin(); it != path.polyline.points.end() - 1; ++it) {
polygon.points.push_back(it->to_point());
}
}
return polygon;
}
@@ -168,7 +185,7 @@ bool ExtrusionLoop::split_at_vertex(const Point &point, const double scaled_epsi
if (int idx = path->polyline.find_point(point, scaled_epsilon); idx != -1) {
if (this->paths.size() == 1) {
// just change the order of points
Polyline p1, p2;
Polyline3 p1, p2;
path->polyline.split_at_index(idx, &p1, &p2);
if (p1.is_valid() && p2.is_valid()) {
p2.append(std::move(p1));
@@ -178,7 +195,7 @@ bool ExtrusionLoop::split_at_vertex(const Point &point, const double scaled_epsi
} else {
// new paths list starts with the second half of current path
ExtrusionPaths new_paths;
Polyline p1, p2;
Polyline3 p1, p2;
path->polyline.split_at_index(idx, &p1, &p2);
new_paths.reserve(this->paths.size() + 1);
{
@@ -218,16 +235,17 @@ ExtrusionLoop::ClosestPathPoint ExtrusionLoop::get_closest_path_and_point(const
ClosestPathPoint best_non_overhang{0, 0};
double min2_non_overhang = std::numeric_limits<double>::max();
for (const ExtrusionPath &path : this->paths) {
std::pair<int, Point> foot_pt_ = foot_pt(path.polyline.points, point);
double d2 = (foot_pt_.second - point).cast<double>().squaredNorm();
std::pair<int, Point3> foot_pt_ = foot_pt(path.polyline.points, Point3(point));
Point foot_pt_2d = Point(foot_pt_.second.x(), foot_pt_.second.y());
double d2 = (foot_pt_2d - point).cast<double>().squaredNorm();
if (d2 < min2) {
out.foot_pt = foot_pt_.second;
out.foot_pt = foot_pt_2d;
out.path_idx = &path - &this->paths.front();
out.segment_idx = foot_pt_.first;
min2 = d2;
}
if (prefer_non_overhang && !is_bridge(path.role()) && d2 < min2_non_overhang) {
best_non_overhang.foot_pt = foot_pt_.second;
best_non_overhang.foot_pt = foot_pt_2d;
best_non_overhang.path_idx = &path - &this->paths.front();
best_non_overhang.segment_idx = foot_pt_.first;
min2_non_overhang = d2;
@@ -249,16 +267,18 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const
// Snap p to start or end of segment_idx if closer than scaled_epsilon.
{
const Point *p1 = this->paths[path_idx].polyline.points.data() + segment_idx;
const Point *p2 = p1;
const Point3 *p1 = this->paths[path_idx].polyline.points.data() + segment_idx;
const Point3 *p2 = p1;
++p2;
double d2_1 = (point - *p1).cast<double>().squaredNorm();
double d2_2 = (point - *p2).cast<double>().squaredNorm();
Point p1_2d = Point(p1->x(), p1->y());
Point p2_2d = Point(p2->x(), p2->y());
double d2_1 = (point - p1_2d).cast<double>().squaredNorm();
double d2_2 = (point - p2_2d).cast<double>().squaredNorm();
const double thr2 = scaled_epsilon * scaled_epsilon;
if (d2_1 < d2_2) {
if (d2_1 < thr2) p = *p1;
if (d2_1 < thr2) p = p1_2d;
} else {
if (d2_2 < thr2) p = *p2;
if (d2_2 < thr2) p = p2_2d;
}
}
@@ -411,16 +431,16 @@ ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths,
: ExtrusionLoop(role)
{
// create slopes
const auto add_slop = [this, slope_max_segment_length, seam_gap](const ExtrusionPath &path, const Polyline &poly, double ratio_begin, double ratio_end) {
const auto add_slop = [this, slope_max_segment_length, seam_gap](const ExtrusionPath &path, const Polyline3 &poly, double ratio_begin, double ratio_end) {
if (poly.empty()) { return; }
// Ensure `slope_max_segment_length`
Polyline detailed_poly;
Polyline3 detailed_poly;
{
detailed_poly.append(poly.first_point());
// Recursively split the line into half until no longer than `slope_max_segment_length`
const std::function<void(const Line &)> handle_line = [slope_max_segment_length, &detailed_poly, &handle_line](const Line &line) {
const std::function<void(const Line3 &)> handle_line = [slope_max_segment_length, &detailed_poly, &handle_line](const Line3 &line) {
if (line.length() <= slope_max_segment_length) {
detailed_poly.append(line.b);
} else {
@@ -441,8 +461,8 @@ ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths,
const auto seg_length = detailed_poly.length();
if (seg_length > seam_gap) {
// Split the segment and remove the last `seam_gap` bit
const Polyline orig = detailed_poly;
Polyline tmp;
const Polyline3 orig = detailed_poly;
Polyline3 tmp;
orig.split_at_length(seg_length - seam_gap, &detailed_poly, &tmp);
ratio_end = lerp(ratio_begin, ratio_end, (seg_length - seam_gap) / seg_length);
@@ -464,8 +484,8 @@ ExtrusionLoopSloped::ExtrusionLoopSloped(ExtrusionPaths& original_paths,
const double path_len = unscale_(path->length());
if (path_len > remaining_length) {
// Split current path into slope and non-slope part
Polyline slope_path;
Polyline flat_path;
Polyline3 slope_path;
Polyline3 flat_path;
path->polyline.split_at_length(scale_(remaining_length), &slope_path, &flat_path);
add_slop(*path, slope_path, start_ratio, 1);
@@ -631,4 +651,28 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role)
return erNone;
}
// ExtrusionPathContoured implementation
ExtrusionEntity *ExtrusionPathContoured::clone() const {
return new ExtrusionPathContoured(*this);
}
ExtrusionEntity *ExtrusionPathContoured::clone_move() {
return new ExtrusionPathContoured(std::move(*this));
}
void ExtrusionPathContoured::simplify(double tolerance) {
// Do not simplify contoured paths
return;
}
void ExtrusionPathContoured::simplify_by_fitting_arc(double tolerance) {
// Do not simplify contoured paths
return;
}
void ExtrusionPathContoured::reverse() {
this->polyline.reverse();
std::reverse(this->z_diffs.begin(), this->z_diffs.end());
}
}

View File

@@ -116,8 +116,8 @@ public:
virtual ExtrusionEntity* clone_move() = 0;
virtual ~ExtrusionEntity() {}
virtual void reverse() = 0;
virtual const Point& first_point() const = 0;
virtual const Point& last_point() const = 0;
virtual Point first_point() const = 0;
virtual Point last_point() const = 0;
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const = 0;
@@ -150,13 +150,17 @@ typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
class ExtrusionPath : public ExtrusionEntity
{
public:
Polyline polyline;
Polyline3 polyline;
double overhang_degree = 0;
int curve_degree = 0;
// Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator.
double mm3_per_mm;
// Width of the extrusion, used for visualization purposes.
float width;
// Height of the extrusion, used for visualization purposes.
float height;
double smooth_speed = 0;
bool z_contoured = false;
ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1), m_role(erNone), m_no_extrusion(false) {}
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role), m_no_extrusion(false) {}
@@ -164,36 +168,52 @@ public:
ExtrusionPath(const ExtrusionPath &rhs)
: polyline(rhs.polyline)
, overhang_degree(rhs.overhang_degree)
, curve_degree(rhs.curve_degree)
, mm3_per_mm(rhs.mm3_per_mm)
, width(rhs.width)
, height(rhs.height)
, smooth_speed(rhs.smooth_speed)
, z_contoured(rhs.z_contoured)
, m_can_reverse(rhs.m_can_reverse)
, m_role(rhs.m_role)
, m_no_extrusion(rhs.m_no_extrusion)
{}
ExtrusionPath(ExtrusionPath &&rhs)
: polyline(std::move(rhs.polyline))
, overhang_degree(rhs.overhang_degree)
, curve_degree(rhs.curve_degree)
, mm3_per_mm(rhs.mm3_per_mm)
, width(rhs.width)
, height(rhs.height)
, smooth_speed(rhs.smooth_speed)
, z_contoured(rhs.z_contoured)
, m_can_reverse(rhs.m_can_reverse)
, m_role(rhs.m_role)
, m_no_extrusion(rhs.m_no_extrusion)
{}
ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs)
ExtrusionPath(const Polyline3 &polyline, const ExtrusionPath &rhs)
: polyline(polyline)
, overhang_degree(rhs.overhang_degree)
, curve_degree(rhs.curve_degree)
, mm3_per_mm(rhs.mm3_per_mm)
, width(rhs.width)
, height(rhs.height)
, smooth_speed(rhs.smooth_speed)
, z_contoured(rhs.z_contoured)
, m_can_reverse(rhs.m_can_reverse)
, m_role(rhs.m_role)
, m_no_extrusion(rhs.m_no_extrusion)
{}
ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs)
ExtrusionPath(Polyline3 &&polyline, const ExtrusionPath &rhs)
: polyline(std::move(polyline))
, overhang_degree(rhs.overhang_degree)
, curve_degree(rhs.curve_degree)
, mm3_per_mm(rhs.mm3_per_mm)
, width(rhs.width)
, height(rhs.height)
, smooth_speed(rhs.smooth_speed)
, z_contoured(rhs.z_contoured)
, m_can_reverse(rhs.m_can_reverse)
, m_role(rhs.m_role)
, m_no_extrusion(rhs.m_no_extrusion)
@@ -206,6 +226,10 @@ public:
this->mm3_per_mm = rhs.mm3_per_mm;
this->width = rhs.width;
this->height = rhs.height;
this->smooth_speed = rhs.smooth_speed;
this->z_contoured = rhs.z_contoured;
this->overhang_degree = rhs.overhang_degree;
this->curve_degree = rhs.curve_degree;
this->polyline = rhs.polyline;
return *this;
}
@@ -216,6 +240,10 @@ public:
this->mm3_per_mm = rhs.mm3_per_mm;
this->width = rhs.width;
this->height = rhs.height;
this->smooth_speed = rhs.smooth_speed;
this->z_contoured = rhs.z_contoured;
this->overhang_degree = rhs.overhang_degree;
this->curve_degree = rhs.curve_degree;
this->polyline = std::move(rhs.polyline);
return *this;
}
@@ -224,8 +252,10 @@ public:
// Create a new object, initialize it with this object using the move semantics.
ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); }
void reverse() override { this->polyline.reverse(); }
const Point& first_point() const override { return this->polyline.points.front(); }
const Point& last_point() const override { return this->polyline.points.back(); }
Point first_point() const override { return this->polyline.points.front().to_point(); }
Point3 first_point3() const { return this->polyline.points.front(); }
Point last_point() const override { return this->polyline.points.back().to_point(); }
Point3 last_point3() const { return this->polyline.points.back(); }
size_t size() const { return this->polyline.size(); }
bool empty() const { return this->polyline.empty(); }
bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
@@ -236,7 +266,7 @@ public:
// Currently not used.
void subtract_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance);
void simplify(double tolerance);
virtual void simplify(double tolerance);
double length() const override;
ExtrusionRole role() const override { return m_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
@@ -252,9 +282,10 @@ public:
{ Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; }
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const override { return this->mm3_per_mm; }
Polyline as_polyline() const override { return this->polyline; }
void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
void collect_points(Points &dst) const override { append(dst, this->polyline.points); }
Polyline as_polyline() const override { return this->polyline.to_polyline(); }
void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline.to_polyline()); }
void collect_points(Points &dst) const override;
void collect_points3(Points3 &dst) const { append(dst, this->polyline.points); }
double total_volume() const override { return mm3_per_mm * unscale<double>(length()); }
//BBS: add new simplifing method by fitting arc
@@ -274,6 +305,23 @@ private:
bool m_no_extrusion = false;
};
class ExtrusionPathContoured : public ExtrusionPath {
public:
std::vector<double> z_diffs;
ExtrusionPathContoured(Polyline3 &&polyline, const ExtrusionPath &rhs, std::vector<double> &&z_diffs)
: ExtrusionPath(std::move(polyline), rhs), z_diffs(std::move(z_diffs))
{}
virtual ExtrusionEntity *clone() const override;
virtual ExtrusionEntity *clone_move() override;
void simplify(double tolerance) override;
virtual void simplify_by_fitting_arc(double tolerance);
void reverse() override;
};
class ExtrusionPathSloped : public ExtrusionPath
{
public:
@@ -292,10 +340,10 @@ public:
ExtrusionPathSloped(ExtrusionPath&& rhs, const Slope& begin, const Slope& end)
: ExtrusionPath(std::move(rhs)), slope_begin(begin), slope_end(end)
{}
ExtrusionPathSloped(const Polyline& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
ExtrusionPathSloped(const Polyline3& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
: ExtrusionPath(polyline, rhs), slope_begin(begin), slope_end(end)
{}
ExtrusionPathSloped(Polyline&& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
ExtrusionPathSloped(Polyline3&& polyline, const ExtrusionPath& rhs, const Slope& begin, const Slope& end)
: ExtrusionPath(std::move(polyline), rhs), slope_begin(begin), slope_end(end)
{}
@@ -354,8 +402,8 @@ public:
// Create a new object, initialize it with this object using the move semantics.
ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); }
void reverse() override;
const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
const Point& last_point() const override { return this->paths.back().polyline.points.back(); }
Point first_point() const override { return this->paths.front().polyline.points.front().to_point(); }
Point last_point() const override { return this->paths.back().polyline.points.back().to_point(); }
size_t size() const { return this->paths.size(); }
bool empty() const { return this->paths.empty(); }
double length() const override;
@@ -379,7 +427,7 @@ public:
size_t n = std::accumulate(paths.begin(), paths.end(), 0, [](const size_t n, const ExtrusionPath &p){ return n + p.polyline.size(); });
dst.reserve(dst.size() + n);
for (const ExtrusionPath &p : this->paths)
append(dst, p.polyline.points);
append(dst, to_points(p.polyline.points));
}
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
@@ -410,8 +458,8 @@ public:
bool is_clockwise() { return this->polygon().is_clockwise(); }
bool is_counter_clockwise() { return this->polygon().is_counter_clockwise(); }
void reverse() override;
const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); }
Point first_point() const override { return this->paths.front().polyline.points.front().to_point(); }
Point last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back().to_point()); return this->first_point(); }
Polygon polygon() const;
double length() const override;
bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled<double>(0.001));
@@ -449,7 +497,7 @@ public:
size_t n = std::accumulate(paths.begin(), paths.end(), 0, [](const size_t n, const ExtrusionPath &p){ return n + p.polyline.size(); });
dst.reserve(dst.size() + n);
for (const ExtrusionPath &p : this->paths)
append(dst, p.polyline.points);
append(dst, to_points(p.polyline.points));
}
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
// check if the loop is smooth, angle_threshold is in radians, default is 10 degrees
@@ -495,7 +543,7 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, Ex
for (Polyline &polyline : polylines)
if (polyline.is_valid()) {
dst.emplace_back(role, mm3_per_mm, width, height);
dst.back().polyline = polyline;
dst.back().polyline = Polyline3(polyline);
}
}
@@ -505,7 +553,7 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, E
for (Polyline &polyline : polylines)
if (polyline.is_valid()) {
dst.emplace_back(role, mm3_per_mm, width, height);
dst.back().polyline = std::move(polyline);
dst.back().polyline = Polyline3(std::move(polyline));
}
polylines.clear();
}
@@ -515,7 +563,7 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polyline &&polyline, Ext
dst.reserve(dst.size() + 1);
if (polyline.is_valid()) {
dst.emplace_back(role, mm3_per_mm, width, height);
dst.back().polyline = std::move(polyline);
dst.back().polyline = Polyline3(std::move(polyline));
}
}
@@ -526,7 +574,7 @@ inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines
if (polyline.is_valid()) {
ExtrusionPath *extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height);
dst.push_back(extrusion_path);
extrusion_path->polyline = polyline;
extrusion_path->polyline = Polyline3(polyline);
}
}
@@ -537,7 +585,7 @@ inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines
if (polyline.is_valid()) {
ExtrusionPath *extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height);
dst.push_back(extrusion_path);
extrusion_path->polyline = std::move(polyline);
extrusion_path->polyline = Polyline3(std::move(polyline));
}
polylines.clear();
}
@@ -557,7 +605,7 @@ inline void extrusion_entities_append_paths_with_wipe(ExtrusionEntitiesPtr &dst,
Point temp = polyline.first_point() - last_end_point;
if (Vec2d(temp.x(), temp.y()).norm() <= 3 * scaled(width)) {
multi_path->paths.emplace_back(role, mm3_per_mm, width, height, true);
multi_path->paths.back().polyline = std::move(Polyline(last_end_point, polyline.first_point()));
multi_path->paths.back().polyline = Polyline3(Polyline(last_end_point, polyline.first_point()));
} else {
dst.push_back(multi_path);
multi_path = new ExtrusionMultiPath();
@@ -565,9 +613,9 @@ inline void extrusion_entities_append_paths_with_wipe(ExtrusionEntitiesPtr &dst,
}
multi_path->paths.emplace_back(role, mm3_per_mm, width, height);
multi_path->paths.back().polyline = std::move(polyline);
multi_path->paths.back().polyline = Polyline3(std::move(polyline));
last_end_point_valid = true;
last_end_point = multi_path->paths.back().polyline.last_point();
last_end_point = multi_path->paths.back().polyline.last_point().to_point();
}
}
if (!multi_path->empty())
@@ -582,7 +630,9 @@ inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons
for (Polygon &poly : loops) {
if (poly.is_valid()) {
ExtrusionPath path(role, mm3_per_mm, width, height);
path.polyline.points = std::move(poly.points);
path.polyline.points.reserve(poly.points.size() + 1);
for (const Point &pt : poly.points)
path.polyline.points.emplace_back(Point3(pt, 0));
path.polyline.points.push_back(path.polyline.points.front());
dst.emplace_back(new ExtrusionLoop(std::move(path)));
}
@@ -597,11 +647,11 @@ inline void extrusion_entities_append_loops_and_paths(ExtrusionEntitiesPtr &dst,
if (polyline.is_valid()) {
if (polyline.is_closed()) {
ExtrusionPath extrusion_path(role, mm3_per_mm, width, height);
extrusion_path.polyline = std::move(polyline);
extrusion_path.polyline = Polyline3(std::move(polyline));
dst.emplace_back(new ExtrusionLoop(std::move(extrusion_path)));
} else {
ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
extrusion_path->polyline = std::move(polyline);
extrusion_path->polyline = Polyline3(std::move(polyline));
dst.emplace_back(extrusion_path);
}
}

View File

@@ -118,8 +118,8 @@ public:
ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const
{ return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); }
void reverse() override;
const Point& first_point() const override { return this->entities.front()->first_point(); }
const Point& last_point() const override { return this->entities.back()->last_point(); }
Point first_point() const override { return this->entities.front()->first_point(); }
Point last_point() const override { return this->entities.back()->last_point(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;

View File

@@ -962,7 +962,7 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const
w = scale_(path.mm3_per_mm / path.height) * scalex;
// printf("scalex: %f, scaley: %f\n", scalex, scaley);
// printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y);
for (Points::const_iterator it = path.polyline.points.begin(); it != path.polyline.points.end(); ++ it) {
for (Points3::const_iterator it = path.polyline.points.begin(); it != path.polyline.points.end(); ++ it) {
// printf("point %d,%d\n", it->x+shift.x(), it->y+shift.y);
ExtrusionPoint ept;
ept.center = V2f(float((*it)(0)+shift.x()-bbox.min.x()) * scalex, float((*it)(1)+shift.y()-bbox.min.y()) * scaley);

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;

View File

@@ -22,10 +22,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>
@@ -5289,12 +5291,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;
}
@@ -5371,11 +5373,11 @@ 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, 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;
@@ -5447,13 +5449,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
@@ -5500,7 +5503,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);
@@ -5594,10 +5597,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
}
}
@@ -5607,8 +5612,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;
@@ -5622,8 +5629,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();
@@ -5635,7 +5642,8 @@ 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 &center3 = 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);
}
@@ -5643,11 +5651,11 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
return gcode;
}
std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string description, double speed)
std::string GCode::extrude_multi_path(const ExtrusionMultiPath &multipath, 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
@@ -5664,8 +5672,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
@@ -5676,13 +5684,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();
}
@@ -5703,7 +5713,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, 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;
@@ -5711,17 +5721,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();
@@ -5764,11 +5774,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(&region - &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);
@@ -5799,7 +5809,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");
@@ -5965,13 +5975,23 @@ 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) {
Point3 first_point = path.first_point3();
if (!m_last_pos_defined || m_last_pos != first_point || m_need_change_layer_lift_z || slope_need_z_travel) {
const bool _last_pos_undefined = !m_last_pos_defined;
double z = DBL_MAX;
if (sloped != nullptr) {
z = get_sloped_z(sloped->slope_begin.z_ratio);
} else if ((!m_last_pos_defined && first_point.z() != 0) || m_last_pos.z() != first_point.z()) {
z = m_nominal_z + unscale_(first_point.z());
if (z < 0.1) {
throw RuntimeError("GCode: very low z");
}
}
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)
"move to first " + description + " point; size " + std::to_string(path.polyline.size()),
z
);
m_need_change_layer_lift_z = false;
// Orca: ensure Z matches planned layer height
@@ -6536,10 +6556,10 @@ 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()) {
for (const Line3& line : path.polyline.lines()) {
std::string tempDescription = description;
const double line_length = line.length() * SCALING_FACTOR;
if (line_length < EPSILON)
@@ -6554,16 +6574,37 @@ 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,
tempDescription + "; z_diff " + std::to_string(z_diff) + " " + ExtrusionEntity::role_to_string(path.role()) + "; eratio " + std::to_string(extrusion_ratio));
} 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,
@@ -6582,7 +6623,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;

View File

@@ -221,7 +221,7 @@ public:
const Vec2d& origin() const { return m_origin; }
void set_origin(const Vec2d &pointf);
void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); }
const Point& last_pos() const { return m_last_pos; }
Point last_pos() const { return m_last_pos.to_point(); }
Vec2d point_to_gcode(const Point &point) const;
Point gcode_to_point(const Vec2d &point) const;
Vec2d point_to_gcode_quantized(const Point& point) const;
@@ -381,7 +381,8 @@ private:
void check_placeholder_parser_failed();
size_t get_extruder_id(unsigned int filament_id) const;
void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; }
void set_last_pos(const Point &pos) { m_last_pos = Point3(pos, 0); m_last_pos_defined = true; }
void set_last_pos(const Point3 &pos) { m_last_pos = pos; m_last_pos_defined = true; }
bool last_pos_defined() const { return m_last_pos_defined; }
void set_extruders(const std::vector<unsigned int> &extruder_ids);
std::string preamble();
@@ -392,9 +393,9 @@ private:
std::string extrude_entity(const ExtrusionEntity &entity, std::string description = "", double speed = -1., const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr());
// Orca: pass the complete collection of region perimeters to the extrude loop to check whether the wipe before external loop
// should be executed
std::string extrude_loop(ExtrusionLoop loop, std::string description, double speed = -1., const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr(), const Point* start_point = nullptr);
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
std::string extrude_loop(const ExtrusionLoop &loop, std::string description, double speed = -1., const ExtrusionEntitiesPtr& region_perimeters = ExtrusionEntitiesPtr(), const Point* start_point = nullptr);
std::string extrude_multi_path(const ExtrusionMultiPath &multipath, std::string description = "", double speed = -1.);
std::string extrude_path(const ExtrusionPath &path, std::string description = "", double speed = -1.);
// Orca: Adaptive PA variables
// Used for adaptive PA when extruding paths with multiple, varying flow segments.
@@ -582,7 +583,7 @@ private:
std::map<std::string, std::vector<std::string>> m_placeholder_error_messages;
#endif
Point m_last_pos;
Point3 m_last_pos;
bool m_last_pos_defined;
std::unique_ptr<CoolingBuffer> m_cooling_buffer;

View File

@@ -87,7 +87,7 @@ public:
for (int i = b; i < e; ++i) {
for (const ExtrusionPath &path : _piles[i].paths) {
if (path.is_force_no_extrusion() == false) {
Polyline check_polyline = path.polyline;
Polyline check_polyline = path.polyline.to_polyline();
check_polyline.translate(_offset);
Lines tmpLines = check_polyline.lines();
for (const Line &line : tmpLines) { lines.emplace_back(line, _id, path.role()); }

View File

@@ -81,7 +81,19 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
if (input_points.empty())
return {};
float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f;
auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast<double>(); };
auto maybe_unscale = [](const P &p) -> Vec2d {
if constexpr (P::RowsAtCompileTime == 3) {
// 3D point - extract XY only
if constexpr (SCALED_INPUT) {
return unscaled(p).template head<2>();
} else {
return p.template head<2>().template cast<double>();
}
} else {
// 2D point - use as is
return SCALED_INPUT ? unscaled(p) : p.template cast<double>();
}
};
std::vector<ExtendedPoint> points;
points.reserve(input_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1));

View File

@@ -30,7 +30,7 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c
static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path)
{
BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)));
BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline.to_polyline(), coord_t(scale_(0.5 * extrusion_path.width)));
BoundingBoxf bboxf;
if (! empty(bbox)) {
bboxf.min = unscale(bbox.min);
@@ -44,7 +44,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio
{
BoundingBox bbox;
for (const ExtrusionPath &extrusion_path : extrusion_loop.paths)
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))));
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline.to_polyline(), coord_t(scale_(0.5 * extrusion_path.width))));
BoundingBoxf bboxf;
if (! empty(bbox)) {
bboxf.min = unscale(bbox.min);
@@ -58,7 +58,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext
{
BoundingBox bbox;
for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths)
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))));
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline.to_polyline(), coord_t(scale_(0.5 * extrusion_path.width))));
BoundingBoxf bboxf;
if (! empty(bbox)) {
bboxf.min = unscale(bbox.min);

View File

@@ -1514,7 +1514,8 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop,
current.path_idx = next_idx_modulo(current.path_idx, loop.paths.size());
current.segment_idx = 0;
}
current.foot_pt = loop.paths[current.path_idx].polyline.points[current.segment_idx];
const Point3 &p3 = loop.paths[current.path_idx].polyline.points[current.segment_idx];
current.foot_pt = Point(p3.x(), p3.y());
return current;
};
@@ -1527,7 +1528,8 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop,
size_t closest_perimeter_point_index = 0;
{ // local space for the closest_perimeter_point_index
Perimeter *closest_perimeter = nullptr;
ExtrusionLoop::ClosestPathPoint closest_point{0,0,loop.paths[0].polyline.points[0]};
const Point3 &init_p3 = loop.paths[0].polyline.points[0];
ExtrusionLoop::ClosestPathPoint closest_point{0,0,Point(init_p3.x(), init_p3.y())};
size_t points_count = std::accumulate(loop.paths.begin(), loop.paths.end(), 0, [](size_t acc,const ExtrusionPath& p) {
return acc + p.polyline.points.size();
});

View File

@@ -26,6 +26,10 @@ namespace FillLightning {
class Generator;
};
namespace sla {
class IndexedMesh;
};
class LayerRegion
{
public:
@@ -191,6 +195,7 @@ public:
FillAdaptive::Octree *support_fill_octree,
FillLightning::Generator* lightning_generator) const;
void make_ironing();
void make_contour_z(const sla::IndexedMesh &mesh);
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;

View File

@@ -224,17 +224,23 @@ using CurledLines = std::vector<CurledLine>;
class Line3
{
public:
Line3() : a(Vec3crd::Zero()), b(Vec3crd::Zero()) {}
Line3(const Vec3crd& _a, const Vec3crd& _b) : a(_a), b(_b) {}
Line3() : a(Point3()), b(Point3()) {}
Line3(const Point3& _a, const Point3& _b) : a(_a), b(_b) {}
// Backward compatibility with Vec3crd
Line3(const Vec3crd& _a, const Vec3crd& _b) : a(Point3(_a)), b(Point3(_b)) {}
double length() const { return (this->a - this->b).cast<double>().norm(); }
Vec3crd vector() const { return this->b - this->a; }
Point3 vector() const { Vec3crd v = this->b - this->a; return Point3(v.x(), v.y(), v.z()); }
Point3 midpoint() const { return Point3((this->a.x() + this->b.x()) / 2, (this->a.y() + this->b.y()) / 2, (this->a.z() + this->b.z()) / 2); }
Vec3crd a;
Vec3crd b;
// Convert to 2D line by dropping Z coordinate
Line to_line() const { return Line(this->a.to_point(), this->b.to_point()); }
Point3 a;
Point3 b;
static const constexpr int Dim = 3;
using Scalar = Vec3crd::Scalar;
using Scalar = coord_t;
};
class Linef
@@ -243,6 +249,10 @@ public:
Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {}
Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {}
Vec2d vector() const { return this->b - this->a; }
Vec2d unit_vector() const { return (length() == 0.0) ? Vec2d::Zero() : vector().normalized(); }
double length() const { return vector().norm(); }
Vec2d a;
Vec2d b;
@@ -263,6 +273,32 @@ public:
Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); }
double length() const { return vector().norm(); }
double distance_to_infinite_squared(const Vec3d &point, Vec3d *closest_point) const {
const Vec3d v = this->b - this->a;
const Vec3d va = point - this->a;
const double l2 = v.squaredNorm();
if (l2 == 0.) {
// a == b case
*closest_point = this->a;
return va.squaredNorm();
}
// Consider the line extending the segment, parameterized as a + t (b - a).
// Find parameter value t of the projection of point onto the line.
const double t = va.dot(v) / l2;
*closest_point = this->a + t * v;
return (point - *closest_point).squaredNorm();
}
double distance_to_infinite_squared(const Vec3d &point) const {
Vec3d nearest_point;
return distance_to_infinite_squared(point, &nearest_point);
}
static inline double distance_to_infinite_squared(const Vec3d &point, const Vec3d &a, const Vec3d &b) {
Linef3 line{a, b};
return line.distance_to_infinite_squared(point);
}
Vec3d a;
Vec3d b;

View File

@@ -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 &center)
{
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;
}
}

View File

@@ -119,18 +119,46 @@ class MultiPoint3
public:
Points3 points;
void append(const Vec3crd& point) { this->points.push_back(point); }
void append(const Point3& point) { this->points.push_back(point); }
void append(const Vec3crd& point) { this->points.push_back(Point3(point)); }
void translate(double x, double y);
void translate(const Point& vector);
void reverse() { std::reverse(this->points.begin(), this->points.end()); }
void rotate(double angle) { this->rotate(cos(angle), sin(angle)); }
void rotate(double cos_angle, double sin_angle);
void rotate(double angle, const Point3 &center);
Point3& first_point() { return this->points.front(); }
Point3& last_point() { return this->points.back(); }
const Point3& first_point() const { return this->points.front(); }
const Point3& last_point() const { return this->points.back(); }
size_t size() const { return this->points.size(); }
bool empty() const { return this->points.empty(); }
void clear() { this->points.clear(); }
auto begin() { return this->points.begin(); }
auto end() { return this->points.end(); }
auto begin() const { return this->points.begin(); }
auto end() const { return this->points.end(); }
virtual Lines3 lines() const = 0;
double length() const;
bool is_valid() const { return this->points.size() >= 2; }
BoundingBox3 bounding_box() const;
// Find a point in the points array
int find_point(const Point &point) const;
int find_point(const Point &point, const double scaled_epsilon) const;
int find_point(const Point3 &point) const;
int find_point(const Point3 &point, const double scaled_epsilon) const;
// Remove exact duplicates, return true if any duplicate has been removed.
bool remove_duplicate_points();
// Douglas-Peucker simplification
static Points3 _douglas_peucker(const Points3 &points, double tolerance);
};
extern BoundingBox get_extents(const MultiPoint &mp);

View File

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

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

View File

@@ -21,8 +21,13 @@ namespace Slic3r {
class BoundingBox;
class BoundingBoxf;
class Line;
class Line3;
class MultiPoint;
class MultiPoint3;
class Point;
class Point3;
class Polyline;
class Polyline3;
using Vector = Point;
// Base template for eigen derived vectors
@@ -57,7 +62,7 @@ using PointsAllocator = tbb::scalable_allocator<BaseType>;
using Points = std::vector<Point, PointsAllocator<Point>>;
using PointPtrs = std::vector<Point*>;
using PointConstPtrs = std::vector<const Point*>;
using Points3 = std::vector<Vec3crd>;
using Points3 = std::vector<Point3>;
using Pointfs = std::vector<Vec2d>;
using Vec2ds = std::vector<Vec2d>;
using Pointf3s = std::vector<Vec3d>;
@@ -79,6 +84,11 @@ using Transform2d = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAli
using Transform3f = Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>;
using Transform3d = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>;
// Utility functions for Point/Polyline conversion
Polyline to_polyline(const Points &points);
Polyline3 to_polyline(const Points3 &points);
Points to_points(const Points3 &points);
// using ColorRGBA = std::array<float, 4>;
// I don't know why Eigen::Transform::Identity() return a const object...
template<int N, class T> Transform<N, T> identity() { return Transform<N, T>::Identity(); }
@@ -258,6 +268,115 @@ inline Point operator* (const Point& l, const double& r)
return { coord_t(l.x() * r), coord_t(l.y() * r) };
}
// Point3 class - 3D point with Z coordinate for non-planar printing (ZAA)
class Point3 : public Vec3crd {
public:
using coord_type = coord_t;
Point3() : Vec3crd(0, 0, 0) {}
Point3(int32_t x, int32_t y, int32_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {}
Point3(int64_t x, int64_t y, int64_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {}
Point3(double x, double y, double z = 0.0) : Vec3crd(coord_t(std::round(x)), coord_t(std::round(y)), coord_t(std::round(z))) {}
Point3(const Point3 &rhs) { *this = rhs; }
explicit Point3(const Point &rhs, coord_t z = 0) : Vec3crd(rhs.x(), rhs.y(), z) {}
explicit Point3(const Vec3crd &vec3crd) : Vec3crd(vec3crd) {}
static Point3 new_scale(coordf_t x, coordf_t y, coordf_t z) {
return Point3(coord_t(scale_(x)), coord_t(scale_(y)), coord_t(scale_(z)));
}
static Point3 new_scale(const Vec3d &v) {
return Point3(coord_t(scale_(v.x())), coord_t(scale_(v.y())), coord_t(scale_(v.z())));
}
static Point3 new_scale(const Vec3f &v) {
return Point3(coord_t(scale_(v.x())), coord_t(scale_(v.y())), coord_t(scale_(v.z())));
}
// Assignment operator for Eigen expressions
template<typename OtherDerived>
Point3& operator=(const Eigen::MatrixBase<OtherDerived> &other)
{
this->Vec3crd::operator=(other);
return *this;
}
Point3& operator+=(const Point3& rhs) { this->x() += rhs.x(); this->y() += rhs.y(); this->z() += rhs.z(); return *this; }
Point3& operator-=(const Point3& rhs) { this->x() -= rhs.x(); this->y() -= rhs.y(); this->z() -= rhs.z(); return *this; }
Point3& operator*=(const double &rhs) {
this->x() = coord_t(this->x() * rhs);
this->y() = coord_t(this->y() * rhs);
this->z() = coord_t(this->z() * rhs);
return *this;
}
Point3 operator*(const double &rhs) const { return Point3(this->x() * rhs, this->y() * rhs, this->z() * rhs); }
bool both_comp(const Point3 &rhs, const std::string& op) {
if (op == ">")
return this->x() > rhs.x() && this->y() > rhs.y();
else if (op == "<")
return this->x() < rhs.x() && this->y() < rhs.y();
return false;
}
bool any_comp(const Point3 &rhs, const std::string &op)
{
if (op == ">")
return this->x() > rhs.x() || this->y() > rhs.y();
else if (op == "<")
return this->x() < rhs.x() || this->y() < rhs.y();
return false;
}
bool any_comp(const coord_t val, const std::string &op)
{
if (op == ">")
return this->x() > val || this->y() > val;
else if (op == "<")
return this->x() < val || this->y() < val;
return false;
}
void rotate(double angle) { this->rotate(std::cos(angle), std::sin(angle)); }
void rotate(double cos_a, double sin_a) {
double cur_x = (double)this->x();
double cur_y = (double)this->y();
this->x() = (coord_t)round(cos_a * cur_x - sin_a * cur_y);
this->y() = (coord_t)round(cos_a * cur_y + sin_a * cur_x);
}
void rotate(double angle, const Point3 &center);
Point3 rotated(double angle) const { Point3 res(*this); res.rotate(angle); return res; }
Point3 rotated(double cos_a, double sin_a) const { Point3 res(*this); res.rotate(cos_a, sin_a); return res; }
Point3 rotated(double angle, const Point3 &center) const { Point3 res(*this); res.rotate(angle, center); return res; }
Point3 rotate_90_degree_ccw() const { return Point3(-this->y(), this->x(), this->z()); }
int nearest_point_index(const Points &points) const;
bool nearest_point(const Points &points, Point3* point) const;
double ccw(const Point3 &p1, const Point3 &p2) const;
double ccw(const Line3 &line) const;
double ccw_angle(const Point3 &p1, const Point3 &p2) const;
Point3 projection_onto(const MultiPoint3 &poly) const;
Point3 projection_onto(const Line3 &line) const;
// Convert to 2D Point by dropping Z coordinate
Point to_point() const {
return Point(this->x(), this->y());
}
double distance_to(const Point3 &point) const { return (point - *this).cast<double>().norm(); }
};
// Utility function to convert Points3 to Points
inline void append_points(Points &dst, const Points3 &src) {
std::transform(src.begin(), src.end(),
std::back_inserter(dst),
[](const Point3 &pt) {
return pt.to_point();
});
}
inline Point3 operator* (const Point3& l, const double& r)
{
return { coord_t(l.x() * r), coord_t(l.y() * r), coord_t(l.z() * r) };
}
inline std::ostream &operator<<(std::ostream &os, const Point &pt)
{
os << unscale_(pt.x()) << "," << unscale_(pt.y());

View File

@@ -612,6 +612,28 @@ std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt)
return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min);
}
std::pair<int, Point3> foot_pt(const Points3 &polyline, const Point3 &pt)
{
if (polyline.size() < 2) return std::make_pair(-1, Point3(0, 0, 0));
auto d2_min = std::numeric_limits<double>::max();
Point3 foot_pt_min;
Point3 prev = polyline.front();
auto it = polyline.begin();
auto it_proj = polyline.begin();
for (++it; it != polyline.end(); ++it) {
Point3 foot_pt = pt.projection_onto(Line3(prev, *it));
double d2 = (foot_pt - pt).cast<double>().squaredNorm();
if (d2 < d2_min) {
d2_min = d2;
foot_pt_min = foot_pt;
it_proj = it;
}
prev = *it;
}
return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min);
}
ThickLines ThickPolyline::thicklines() const
{
ThickLines lines;
@@ -650,4 +672,217 @@ Lines3 Polyline3::lines() const
return lines;
}
// Polyline3 ZAA methods implementation
Polyline Polyline3::to_polyline() const {
Polyline out;
out.points.reserve(this->points.size());
for (const Point3 &point : this->points) {
out.points.emplace_back(point.x(), point.y());
}
return out;
}
void Polyline3::clip_end(double distance) {
size_t remove_after_index = this->size();
while (distance > 0) {
Vec3d last_point = this->last_point().cast<double>();
this->points.pop_back();
remove_after_index--;
if (this->points.empty()) {
this->fitting_result.clear();
return;
}
Vec3d v = this->last_point().cast<double>() - last_point;
double lsqr = v.squaredNorm();
if (lsqr > distance * distance) {
Vec3d result = last_point + v * (distance / sqrt(lsqr));
this->points.emplace_back(Point3(coord_t(result.x()), coord_t(result.y()), coord_t(result.z())));
break;
}
distance -= sqrt(lsqr);
}
// Clear fitting result if it's affected
if (!fitting_result.empty()) {
while (!fitting_result.empty() && fitting_result.back().start_point_index >= remove_after_index)
fitting_result.pop_back();
if (!fitting_result.empty()) {
fitting_result.back().end_point_index = this->points.size() - 1;
}
}
}
void Polyline3::simplify(double tolerance) {
this->points = MultiPoint3::_douglas_peucker(this->points, tolerance);
this->fitting_result.clear();
}
void Polyline3::simplify_by_fitting_arc(double tolerance) {
// For now, just use regular simplify
// Full ZAA implementation would use ArcFitter::do_arc_fitting_and_simplify
this->simplify(tolerance);
}
bool Polyline3::split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) const
{
if (index > this->size() - 1)
return false;
if (index == 0) {
p1->clear();
p1->append(this->first_point());
*p2 = *this;
} else if (index == this->size() - 1) {
p2->clear();
p2->append(this->last_point());
*p1 = *this;
} else {
// Split first part
p1->clear();
p1->points.reserve(index + 1);
p1->points.insert(p1->begin(), this->begin(), this->begin() + index + 1);
Point3 new_endpoint;
if (this->split_fitting_result_before_index(index, new_endpoint, p1->fitting_result))
p1->points.back() = new_endpoint;
// Split second part
p2->clear();
p2->points.reserve(this->size() - index);
p2->points.insert(p2->begin(), this->begin() + index, this->end());
Point3 new_startpoint;
if (this->split_fitting_result_after_index(index, new_startpoint, p2->fitting_result))
p2->points.front() = new_startpoint;
}
return true;
}
void Polyline3::append(const Point3& point) {
// Don't append if same as last point
if (!this->empty() && this->last_point() == point)
return;
this->points.push_back(point);
// Clear fitting result as structure changed
this->fitting_result.clear();
}
void Polyline3::append(const Polyline3 &src) {
if (!src.is_valid()) return;
if (this->points.empty()) {
this->points = src.points;
this->fitting_result = src.fitting_result;
} else {
// Append points
if (!src.points.empty() && !this->points.empty() && this->last_point() == src.points.front()) {
// Skip first point if it's the same as our last point
this->points.insert(this->points.end(), src.points.begin() + 1, src.points.end());
} else {
this->points.insert(this->points.end(), src.points.begin(), src.points.end());
}
// Note: Full arc fitting integration would merge fitting_result here
this->fitting_result.clear();
}
}
void Polyline3::append_before(const Point3& point) {
// Don't append if same as first point
if (!this->empty() && this->first_point() == point)
return;
this->points.insert(this->points.begin(), point);
// Clear fitting result as structure changed
this->fitting_result.clear();
}
void Polyline3::split_at(Point &point, Polyline3* p1, Polyline3* p2) const {
if (this->points.empty()) return;
// Check if the point is on the polyline
int index = this->find_point(point);
if (index != -1) {
// The split point is on the polyline
split_at_index(index, p1, p2);
point = p1->is_valid() ? p1->last_point().to_point() : p2->first_point().to_point();
return;
}
// Find the line to split at
size_t line_idx = 0;
Point p = this->first_point().to_point();
double min = (p - point).cast<double>().norm();
Lines3 lines = this->lines();
for (Lines3::const_iterator line = lines.begin(); line != lines.end(); ++line) {
Point p_tmp = point.projection_onto(line->to_line());
if ((p_tmp - point).cast<double>().norm() < min) {
p = p_tmp;
min = (p - point).cast<double>().norm();
line_idx = line - lines.begin();
}
}
// Judge whether the closest point is one vertex of polyline
index = this->find_point(p);
if (index != -1) {
this->split_at_index(index, p1, p2);
} else {
Polyline3 temp;
this->split_at_index(line_idx, p1, &temp);
p1->append_before(Point3(point, p1->last_point().z()));
this->split_at_index(line_idx + 1, &temp, p2);
p2->append_before(Point3(point, p2->first_point().z()));
}
point = p;
}
void Polyline3::split_at(Point3 &point, Polyline3* p1, Polyline3* p2) const {
Point p = point.to_point();
this->split_at(p, p1, p2);
point = Point3(p, point.z());
}
bool Polyline3::split_at_length(const double length, Polyline3 *p1, Polyline3 *p2) const {
if (this->points.empty()) return false;
if (length < 0 || length > this->length()) { return false; }
if (length < SCALED_EPSILON) {
p1->clear();
p1->append_before(this->first_point());
*p2 = *this;
} else if (is_approx(length, this->length(), SCALED_EPSILON)) {
p2->clear();
p2->append_before(this->last_point());
*p1 = *this;
} else {
// Find the line to split at
size_t line_idx = 0;
double acc_length = 0;
Point p = this->first_point().to_point();
for (const auto &l : this->lines()) {
p = l.b.to_point();
const double current_length = l.length();
if (acc_length + current_length >= length) {
p = lerp(l.a.to_point(), l.b.to_point(), (length - acc_length) / current_length);
break;
}
acc_length += current_length;
line_idx++;
}
// Judge whether the closest point is one vertex of polyline
int index = this->find_point(p);
if (index != -1) {
this->split_at_index(index, p1, p2);
} else {
Polyline3 temp;
this->split_at_index(line_idx, p1, &temp);
p1->append_before(Point3(p, p1->last_point().z()));
this->split_at_index(line_idx + 1, &temp, p2);
p2->append_before(Point3(p, p2->first_point().z()));
}
}
return true;
}
}

View File

@@ -251,6 +251,7 @@ bool remove_degenerate(Polylines &polylines);
// Returns index of a segment of a polyline and foot point of pt on polyline.
std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt);
std::pair<int, Point3> foot_pt(const Points3 &polyline, const Point3 &pt);
class ThickPolyline : public Polyline {
public:
@@ -290,7 +291,68 @@ inline ThickPolylines to_thick_polylines(Polylines&& polylines, const coordf_t w
class Polyline3 : public MultiPoint3
{
public:
Polyline3() {}
explicit Polyline3(const Points3 &points) { this->points = points; }
explicit Polyline3(const Polyline &poly, coord_t z = 0) {
this->points.reserve(poly.points.size());
for (const Point &pt : poly.points) {
this->points.emplace_back(pt.x(), pt.y(), z);
}
}
virtual Lines3 lines() const;
// Convert to 2D Polyline by dropping Z coordinates
Polyline to_polyline() const;
// Clip the end of the polyline by a distance
void clip_end(double distance);
// Simplify polyline using Douglas-Peucker algorithm
void simplify(double tolerance);
// Simplify by arc fitting (for ZAA arc fitting support)
void simplify_by_fitting_arc(double tolerance);
// Reverse the polyline
using MultiPoint3::reverse;
// Split polyline at given index
bool split_at_index(const size_t index, Polyline3 *p1, Polyline3 *p2) const;
// Split polyline at a given point (2D)
void split_at(Point &point, Polyline3* p1, Polyline3* p2) const;
// Split polyline at a given point (3D)
void split_at(Point3 &point, Polyline3* p1, Polyline3* p2) const;
// Split polyline at a given length
bool split_at_length(const double length, Polyline3 *p1, Polyline3 *p2) const;
// Append a single point
void append(const Point3& point);
// Append another Polyline3
void append(const Polyline3& src);
// Append before (prepend)
void append_before(const Point3& point);
// Arc fitting support - fitting_result stores arc path data
// This is populated by simplify_by_fitting_arc()
// Uses the global PathFittingData from ArcFitter.hpp
std::vector<PathFittingData> fitting_result;
private:
// Helper methods for split_at_index
bool split_fitting_result_before_index(size_t index, Point3 &new_endpoint, std::vector<PathFittingData> &result) const {
// Simplified stub - full implementation would handle arc fitting data
return false;
}
bool split_fitting_result_after_index(size_t index, Point3 &new_startpoint, std::vector<PathFittingData> &result) const {
// Simplified stub - full implementation would handle arc fitting data
return false;
}
};
typedef std::vector<Polyline3> Polylines3;

View File

@@ -953,6 +953,8 @@ static std::vector<std::string> s_Preset_print_options {
"enable_wrapping_detection",
"seam_slope_type", "seam_slope_conditional", "scarf_angle_threshold", "scarf_joint_speed", "scarf_joint_flow_ratio", "seam_slope_start_height", "seam_slope_entire_loop", "seam_slope_min_length", "seam_slope_steps", "seam_slope_inner_walls", "scarf_overhang_threshold",
"interlocking_beam", "interlocking_orientation", "interlocking_beam_layer_count", "interlocking_depth", "interlocking_boundary_avoidance", "interlocking_beam_width","calib_flowrate_topinfill_special_order",
// Z Anti-Aliasing (ZAA)
"zaa_enabled", "zaa_minimize_perimeter_height", "zaa_dont_alternate_fill_direction", "zaa_min_z", "zaa_region_disable", "ironing_expansion",
};
static std::vector<std::string> s_Preset_filament_options {/*"filament_colour", */ "default_filament_colour", "required_nozzle_HRC", "filament_diameter", "pellet_flow_coefficient", "volumetric_speed_coefficients", "filament_type",

View File

@@ -2148,6 +2148,17 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
}
}
// Z-Contouring
for (PrintObject *obj : m_objects) {
bool need_contouring = need_slicing_objects.count(obj) != 0 && obj->config().zaa_enabled;
if (need_contouring) {
obj->contour_z();
} else {
if (obj->set_started(posContouring))
obj->set_done(posContouring);
}
}
tbb::parallel_for(tbb::blocked_range<int>(0, int(m_objects.size())),
[this, need_slicing_objects](const tbb::blocked_range<int>& range) {
for (int i = range.begin(); i < range.end(); i++) {
@@ -2186,6 +2197,8 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
obj->set_done(posInfill);
if (obj->set_started(posIroning))
obj->set_done(posIroning);
if (obj->set_started(posContouring))
obj->set_done(posContouring);
if (obj->set_started(posSupportMaterial))
obj->set_done(posSupportMaterial);
if (obj->set_started(posDetectOverhangsForLift))
@@ -2565,7 +2578,7 @@ void Print::_make_skirt()
flow.width(),
(float)initial_layer_print_height // this will be overridden at G-code export time
)));
eloop.paths.back().polyline = loop.split_at_first_point();
eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
m_skirt.append(eloop);
if (m_config.min_skirt_length.value > 0) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
@@ -2623,7 +2636,7 @@ void Print::_make_skirt()
flow.width(),
(float)initial_layer_print_height // this will be overridden at G-code export time
)));
eloop.paths.back().polyline = loop.split_at_first_point();
eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
object->m_skirt.append(std::move(eloop));
if (m_config.min_skirt_length.value > 0) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
@@ -4018,7 +4031,8 @@ static void from_json(const json& j, Polyline& poly_line) {
}
static void from_json(const json& j, ExtrusionPath& extrusion_path) {
extrusion_path.polyline = j[JSON_EXTRUSION_POLYLINE];
Polyline temp_polyline = j[JSON_EXTRUSION_POLYLINE];
extrusion_path.polyline = Polyline3(temp_polyline);
extrusion_path.mm3_per_mm = j[JSON_EXTRUSION_MM3_PER_MM];
extrusion_path.width = j[JSON_EXTRUSION_WIDTH];
extrusion_path.height = j[JSON_EXTRUSION_HEIGHT];
@@ -4852,8 +4866,9 @@ ExtrusionLayers FakeWipeTower::getTrueExtrusionLayersFromWipeTower() const
paths.reserve(it->second.size());
for (auto &polyline : it->second) {
ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, layer_heights[index]);
path.polyline = polyline;
for (auto &p : path.polyline.points) p += trans;
path.polyline = Polyline3(polyline);
Point3 trans3(trans, 0);
for (auto &p : path.polyline.points) p += trans3;
paths.push_back(path);
}
el.paths = std::move(paths);

View File

@@ -92,7 +92,7 @@ enum PrintStep {
enum PrintObjectStep {
posSlice, posPerimeters,posEstimateCurledExtrusions, posPrepareInfill,
posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath,
posInfill, posIroning, posContouring, posSupportMaterial, posSimplifyPath, posSimplifySupportPath,
// BBS
posDetectOverhangsForLift,
posSimplifyWall, posSimplifyInfill,
@@ -496,6 +496,7 @@ private:
void prepare_infill();
void infill();
void ironing();
void contour_z();
void generate_support_material();
void estimate_curled_extrusions();
void simplify_extrusion_path();
@@ -642,14 +643,14 @@ struct FakeWipeTower
std::vector<ExtrusionPaths> paths;
for (float h = 0.f; h < height; h += layer_height) {
ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, layer_height);
path.polyline = {minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner};
path.polyline = Polyline3(Polyline{{minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner}});
paths.push_back({path});
if (h == 0.f) { // add brim
ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, layer_height);
Point wtbminCorner = {minCorner - Point{bd, bd}};
Point wtbmaxCorner = {maxCorner + Point{bd, bd}};
fakeBrim.polyline = {wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner};
fakeBrim.polyline = Polyline3(Polyline{{wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner}});
paths.back().push_back(fakeBrim);
}
}
@@ -686,13 +687,13 @@ struct FakeWipeTower
ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, lh);
path.polyline = { minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner };
path.polyline = Polyline3(Polyline{{ minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }});
paths.push_back({ path });
// We added the border, now add several parallel lines so we can detect an object that is fully inside the tower.
// For now, simply use fixed spacing of 3mm.
for (coord_t y=minCorner.y()+scale_(3.); y<maxCorner.y(); y+=scale_(3.)) {
path.polyline = { {minCorner.x(), y}, {maxCorner.x(), y} };
path.polyline = Polyline3(Polyline{{ {minCorner.x(), y}, {maxCorner.x(), y} }});
paths.back().emplace_back(path);
}

View File

@@ -4005,6 +4005,57 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("ironing_expansion", coFloat);
def->label = L("Ironing expansion");
def->category = L("Quality");
def->tooltip = L("Expand or contract the ironing area.");
def->sidetext = L("mm");
def->min = -100;
def->max = 100;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("zaa_region_disable", coBool);
def->label = L("Disable Z contouring for region");
def->category = L("Quality");
def->tooltip = L("Disable Z contouring for this specific region");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("zaa_enabled", coBool);
def->label = L("Z contouring enabled");
def->category = L("Quality");
def->tooltip = L("Enable Z-layer contouring (aka Z-layer anti-aliasing)");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("zaa_minimize_perimeter_height", coFloat);
def->label = L("Minimize wall height angle");
def->category = L("Quality");
def->tooltip = L("Reduce top surface perimeter heights to match height of edge for perimeters less than this angle. Set 0 to disable.");
def->sidetext = L("°");
def->min = 0;
def->max = 90;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(35));
def = this->add("zaa_dont_alternate_fill_direction", coBool);
def->label = L("Don't alternate fill direction");
def->category = L("Quality");
def->tooltip = L("Disable alternating fill direction when using Z contouring");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("zaa_min_z", coFloat);
def->label = L("Minimum z height");
def->category = L("Quality");
def->tooltip = L("Minimum z layer height. Also controls slicing plane");
def->sidetext = L("mm");
def->min = 0;
def->max = 100;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.05));
def = this->add("layer_change_gcode", coString);
def->label = L("Layer change G-code");
def->tooltip = L("This G-code is inserted at every layer change after the Z lift.");

View File

@@ -1027,6 +1027,11 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInt, interlocking_depth))
((ConfigOptionInt, interlocking_boundary_avoidance))
// Z Anti-Aliasing (aka Z Contouring)
((ConfigOptionBool, zaa_enabled))
((ConfigOptionBool, zaa_dont_alternate_fill_direction))
((ConfigOptionFloat, zaa_min_z))
// Orca: internal use only
((ConfigOptionBool, calib_flowrate_topinfill_special_order)) // ORCA: special flag for flow rate calibration
)
@@ -1101,6 +1106,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, ironing_speed))
((ConfigOptionFloat, ironing_angle))
((ConfigOptionBool, ironing_angle_fixed))
((ConfigOptionFloat, ironing_expansion))
// Filament Ironing
((ConfigOptionPercentsNullable, filament_ironing_flow))
((ConfigOptionFloatsNullable, filament_ironing_spacing))
@@ -1189,6 +1195,10 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloatOrPercent, scarf_joint_speed))
((ConfigOptionFloat, scarf_joint_flow_ratio))
((ConfigOptionPercent, scarf_overhang_threshold))
// Z Anti-Aliasing (aka Z Contouring)
((ConfigOptionBool, zaa_region_disable))
((ConfigOptionFloat, zaa_minimize_perimeter_height))
)
PRINT_CONFIG_CLASS_DEFINE(

View File

@@ -8,6 +8,7 @@
#include "Layer.hpp"
#include "MutablePolygon.hpp"
#include "PrintConfig.hpp"
#include "SLA/IndexedMesh.hpp"
#include "Support/SupportMaterial.hpp"
#include "Support/SupportSpotsGenerator.hpp"
#include "Support/TreeSupport.hpp"
@@ -23,6 +24,10 @@
#include "AABBTreeLines.hpp"
#include <float.h>
#include <iterator>
#include <mutex>
#include <ostream>
#include <string>
#include <oneapi/tbb/blocked_range.h>
#include <oneapi/tbb/concurrent_vector.h>
#include <oneapi/tbb/parallel_for.h>
@@ -709,6 +714,46 @@ void PrintObject::ironing()
}
}
void PrintObject::contour_z()
{
if (!this->set_started(posContouring)) {
return;
}
m_print->set_status(40, L("Z contouring"));
BOOST_LOG_TRIVIAL(debug) << "Contouring in parallel - start";
TriangleMesh mesh = this->m_model_object->raw_mesh();
if (m_model_object->instances.size() != 1) {
throw RuntimeError("ContourZ: unexpected number of instances");
}
m_model_object->instances.front()->transform_mesh(&mesh, true);
sla::IndexedMesh imesh(mesh);
std::mutex mtx;
size_t completed = 0;
tbb::parallel_for(
// Contouring starting with layer second layer to avoid build plate collision
tbb::blocked_range<size_t>(1, m_layers.size()),
[&, this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); layer_idx++) {
m_print->throw_if_canceled();
m_layers[layer_idx]->make_contour_z(imesh);
std::scoped_lock lock(mtx);
completed++;
std::string msg = (boost::format("Z contoured layer %d/%d (%d%%)") % (completed) % m_layers.size() % int(double(completed) / m_layers.size() * 100)).str();
m_print->set_status(40, msg);
}
}
);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Contouring in parallel - end";
this->set_done(posContouring);
}
// BBS
void PrintObject::clear_overhangs_for_lift()
{
@@ -1351,15 +1396,15 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
// propagate to dependent steps
if (step == posPerimeters) {
invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posSimplifyPath, posSimplifyInfill });
invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posContouring, posSimplifyPath, posSimplifyInfill });
invalidated |= m_print->invalidate_steps({ psSkirtBrim });
} else if (step == posPrepareInfill) {
invalidated |= this->invalidate_steps({ posInfill, posIroning, posSimplifyPath, posSimplifyInfill });
invalidated |= this->invalidate_steps({ posInfill, posIroning, posContouring, posSimplifyPath, posSimplifyInfill });
} else if (step == posInfill) {
invalidated |= this->invalidate_steps({ posIroning, posSimplifyInfill });
invalidated |= this->invalidate_steps({ posIroning, posContouring, posSimplifyInfill });
invalidated |= m_print->invalidate_steps({ psSkirtBrim });
} else if (step == posSlice) {
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifyInfill });
invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posContouring, posSupportMaterial, posSimplifyPath, posSimplifyInfill });
invalidated |= m_print->invalidate_steps({ psSkirtBrim });
m_slicing_params.valid = false;
} else if (step == posSupportMaterial) {

View File

@@ -4,6 +4,7 @@
#include "ClipperUtils.hpp"
#include "ElephantFootCompensation.hpp"
#include "Exception.hpp"
#include "I18N.hpp"
#include "Layer.hpp"
#include "MultiMaterialSegmentation.hpp"
@@ -34,6 +35,13 @@ LayerPtrs new_layers(
coordf_t lo = object_layers[i_layer];
coordf_t hi = object_layers[i_layer + 1];
coordf_t slice_z = 0.5 * (lo + hi);
if (print_object->config().zaa_enabled) {
coordf_t z_offset = print_object->config().zaa_min_z;
slice_z = lo + z_offset;
if (slice_z < lo || slice_z > hi) {
throw RuntimeError("Bad min Z value");
}
}
Layer *layer = new Layer(id ++, print_object, hi - lo, hi + zmin, slice_z);
out.emplace_back(layer);
if (prev != nullptr) {

View File

@@ -1000,7 +1000,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_greedy2(SegmentEndPointFunc
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
{
auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
auto segment_end_point = [&entities](size_t idx, bool first_point) -> Point { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); };
auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); };
std::vector<std::pair<size_t, bool>> out = chain_segments_greedy_constrained_reversals<Point, decltype(segment_end_point), decltype(could_reverse)>(segment_end_point, could_reverse, entities.size(), start_near);
for (std::pair<size_t, bool> &segment : out) {
@@ -1028,6 +1028,11 @@ void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const s
entities.swap(out);
}
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point &start_near)
{
chain_and_reorder_extrusion_entities(entities, &start_near);
}
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
{
// this function crashes if there are empty elements in entities
@@ -1038,7 +1043,7 @@ void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entitie
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near)
{
auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); };
auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> Point { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); };
return chain_segments_greedy<Point, decltype(segment_end_point)>(segment_end_point, extrusion_paths.size(), start_near);
}

View File

@@ -20,6 +20,7 @@ std::vector<size_t> chain_expolygons(const ExPolygons &input_exploy);
std::vector<std::pair<size_t, bool>> chain_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const std::vector<std::pair<size_t, bool>> &chain);
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point &start_near);
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near = nullptr);
std::vector<std::pair<size_t, bool>> chain_extrusion_paths(std::vector<ExtrusionPath> &extrusion_paths, const Point *start_near = nullptr);

View File

@@ -1163,7 +1163,7 @@ static void modulate_extrusion_by_overlapping_layers(
for (ExtrusionEntity *ee : extrusions_in_out) {
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(ee);
assert(path != nullptr);
polylines.emplace_back(Polyline(std::move(path->polyline)));
polylines.emplace_back(path->polyline.to_polyline());
path_ends.emplace_back(std::pair<Point, Point>(polylines.back().points.front(), polylines.back().points.back()));
delete path;
}
@@ -1288,9 +1288,10 @@ static void modulate_extrusion_by_overlapping_layers(
if (! path->polyline.points.empty())
path->polyline.points.pop_back();
// Consume the fragment's polyline, remove it from the input fragments, so it will be ignored the next time.
path->polyline.append(std::move(frag_polyline));
path->polyline.append(Polyline3(std::move(frag_polyline)));
frag_polyline.points.clear();
pt_current = path->polyline.points.back();
const Point3 &pt_back3 = path->polyline.points.back();
pt_current = Point(pt_back3.x(), pt_back3.y());
if (pt_current == pt_end) {
// End of the path.
break;

View File

@@ -1181,7 +1181,9 @@ namespace SupportMaterialInternal {
// This is a complete loop.
// Add the outer contour first.
Polygon poly;
poly.points = ep.polyline.points;
// Convert Points3 to Points
for (const Point3 &p3 : ep.polyline.points)
poly.points.emplace_back(p3.x(), p3.y());
poly.points.pop_back();
if (poly.area() < 0)
poly.reverse();

View File

@@ -16,11 +16,11 @@ ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline& thick_polyl
if (line_len < SCALED_EPSILON) {
// The line is so tiny that we don't care about its width when we connect it to another line.
if (!path.empty())
path.polyline.points.back() = line.b; // If the variable path is non-empty, connect this tiny line to it.
path.polyline.points.back() = Point3(line.b); // If the variable path is non-empty, connect this tiny line to it.
else if (i + 1 < (int)lines.size()) // If there is at least one following line, connect this tiny line to it.
lines[i + 1].a = line.a;
else if (!multi_path.paths.empty())
multi_path.paths.back().polyline.points.back() = line.b; // Connect this tiny line to the last finished path.
multi_path.paths.back().polyline.points.back() = Point3(line.b); // Connect this tiny line to the last finished path.
// If any of the above isn't satisfied, then remove this tiny line.
continue;
@@ -65,8 +65,8 @@ ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline& thick_polyl
const double w = fmax(line.a_width, line.b_width);
const Flow new_flow = (role == erOverhangPerimeter && flow.bridge()) ? flow : flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));
if (path.polyline.points.empty()) {
path.polyline.append(line.a);
path.polyline.append(line.b);
path.polyline.append(Point3(line.a));
path.polyline.append(Point3(line.b));
// Convert from spacing to extrusion width based on the extrusion model
// of a square extrusion ended with semi circles.
#ifdef SLIC3R_DEBUG
@@ -81,7 +81,7 @@ ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline& thick_polyl
if (thickness_delta <= merge_tolerance) {
// the width difference between this line and the current flow
// (of the previous line) width is within the accepted tolerance
path.polyline.append(line.b);
path.polyline.append(Point3(line.b));
} else {
// we need to initialize a new line
multi_path.paths.emplace_back(std::move(path));
@@ -124,13 +124,13 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths_2(const ThickPolyline& t
path = ExtrusionPath(role);
double length = lines[start_index].length();
double sum = lines[start_index].length() * 0.5 * (lines[start_index].a_width + lines[start_index].b_width);
path.polyline.append(lines[start_index].a);
path.polyline.append(Point3(lines[start_index].a));
for (int idx = start_index + 1; idx < i; idx++) {
length += lines[idx].length();
sum += lines[idx].length() * 0.5 * (lines[idx].a_width + lines[idx].b_width);
path.polyline.append(lines[idx].a);
path.polyline.append(Point3(lines[idx].a));
}
path.polyline.append(lines[i].a);
path.polyline.append(Point3(lines[i].a));
if (length > SCALED_EPSILON) {
double w = sum / length;
Flow new_flow = flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));
@@ -193,13 +193,13 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths_2(const ThickPolyline& t
path = ExtrusionPath(role);
double length = lines[start_index].length();
double sum = lines[start_index].length() * lines[start_index].a_width;
path.polyline.append(lines[start_index].a);
path.polyline.append(Point3(lines[start_index].a));
for (int idx = start_index + 1; idx < final_size; idx++) {
length += lines[idx].length();
sum += lines[idx].length() * lines[idx].a_width;
path.polyline.append(lines[idx].a);
path.polyline.append(Point3(lines[idx].a));
}
path.polyline.append(lines[final_size - 1].b);
path.polyline.append(Point3(lines[final_size - 1].b));
if (length > SCALED_EPSILON) {
double w = sum / length;
Flow new_flow = flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));

View File

@@ -5,6 +5,7 @@
#define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@"
#define SLIC3R_VERSION "@SLIC3R_VERSION@"
#define SoftFever_VERSION "@SoftFever_VERSION@"
#define ZAA_VERSION "@ZAA_VERSION@"
#ifndef GIT_COMMIT_HASH
#define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized
#endif

View File

@@ -1991,7 +1991,7 @@ void _3DScene::thick_lines_to_verts(
// Fill in the qverts and tverts with quads and triangles for the extrusion_path.
void _3DScene::extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GUI::GLModel::Geometry& geometry)
{
Polyline polyline = extrusion_path.polyline;
Polyline polyline = extrusion_path.polyline.to_polyline();
polyline.remove_duplicate_points();
polyline.translate(copy);
const Lines lines = polyline.lines();
@@ -2007,7 +2007,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, flo
std::vector<double> widths;
std::vector<double> heights;
for (const ExtrusionPath& extrusion_path : extrusion_loop.paths) {
Polyline polyline = extrusion_path.polyline;
Polyline polyline = extrusion_path.polyline.to_polyline();
polyline.remove_duplicate_points();
polyline.translate(copy);
const Lines lines_this = polyline.lines();
@@ -2025,7 +2025,7 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_mult
std::vector<double> widths;
std::vector<double> heights;
for (const ExtrusionPath& extrusion_path : extrusion_multi_path.paths) {
Polyline polyline = extrusion_path.polyline;
Polyline polyline = extrusion_path.polyline.to_polyline();
polyline.remove_duplicate_points();
polyline.translate(copy);
const Lines lines_this = polyline.lines();

View File

@@ -2567,13 +2567,32 @@ void GUI_App::init_single_instance_checker(const std::string &name, const std::s
m_single_instance_checker = std::make_unique<wxSingleInstanceChecker>(boost::nowide::widen(name), boost::nowide::widen(path));
}
bool GUI_App::CallOnInit()
{
// Override wxApp::CallOnInit to catch exceptions from ~wxMacAutoreleasePool
try {
return wxApp::CallOnInit();
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(fatal) << "Exception in CallOnInit: " << e.what();
return false;
} catch (...) {
// The app was initialized, just the autorelease pool cleanup threw.
// Return true to let the app continue.
return m_initialized;
}
}
bool GUI_App::OnInit()
{
try {
return on_init_inner();
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(fatal) << "OnInit Got Fatal error: " << e.what();
generic_exception_handle();
flush_logs();
return false;
} catch (...) {
BOOST_LOG_TRIVIAL(fatal) << "OnInit caught non-std exception";
flush_logs();
return false;
}
}
@@ -3384,8 +3403,16 @@ bool GUI_App::on_init_network(bool try_backup)
Slic3r::NetworkAgentFactory::register_all_agents();
// m_agent = new Slic3r::NetworkAgent(data_directory);
std::unique_ptr<Slic3r::NetworkAgent> agent_ptr = Slic3r::create_agent_from_config(data_directory, app_config);
m_agent = agent_ptr.release();
try {
std::unique_ptr<Slic3r::NetworkAgent> agent_ptr = Slic3r::create_agent_from_config(data_directory, app_config);
m_agent = agent_ptr.release();
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "Failed to create network agent: " << e.what();
m_agent = nullptr;
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "Failed to create network agent: unknown exception (code signing?)";
m_agent = nullptr;
}
if (!m_device_manager)
m_device_manager = new Slic3r::DeviceManager(m_agent);
@@ -5683,6 +5710,7 @@ std::string GUI_App::format_display_version()
if (!version_display.empty()) return version_display;
version_display = SoftFever_VERSION;
version_display += " / ZAA v" + std::string(ZAA_VERSION);
return version_display;
}
@@ -6514,7 +6542,8 @@ void GUI_App::update_mode()
mainframe->m_param_dialog->panel()->update_mode();
if (mainframe->m_printer_view)
mainframe->m_printer_view->update_mode();
mainframe->m_webview->update_mode();
if (mainframe->m_webview)
mainframe->m_webview->update_mode();
#ifdef _MSW_DARK_MODE
if (!wxGetApp().tabs_as_menu())
@@ -6526,8 +6555,6 @@ void GUI_App::update_mode()
for (auto tab : model_tabs_list)
tab->update_mode();
//BBS plater()->update_menus();
plater()->canvas3D()->update_gizmos_on_off_state();
}

View File

@@ -324,6 +324,7 @@ public:
void on_start_subscribe_again(std::string dev_id);
std::string get_local_models_path();
bool OnInit() override;
bool CallOnInit() override;
int OnExit() override;
bool initialized() const { return m_initialized; }
inline bool is_enable_multi_machine() { return this->app_config&& this->app_config->get("enable_multi_machine") == "true"; }

View File

@@ -102,7 +102,9 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::OBJECT_C
std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::PART_CATEGORY_SETTINGS=
{
{ L("Quality"), {{"ironing_type", "",8},{"ironing_flow", "",9},{"ironing_spacing", "",10},{"ironing_inset", "", 11},{"bridge_flow", "",11},{"make_overhang_printable", "",11},{"bridge_density", "", 1}
{ L("Quality"), {{"ironing_type", "",8},{"ironing_flow", "",9},{"ironing_spacing", "",10},{"ironing_inset", "", 11},{"bridge_flow", "",11},{"make_overhang_printable", "",11},{"bridge_density", "", 1},
{"ironing_expansion", "", 14},
{"zaa_enabled", "", 1}, {"zaa_region_disable", "", 2}, {"zaa_minimize_perimeter_height", "", 3}, {"zaa_dont_alternate_fill_direction", "", 4}, {"zaa_min_z", "", 5}
}},
{ L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},{"top_surface_density", L("Top Surface Density"),1},
{"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},{"bottom_surface_density", L("Bottom Surface Density"),1},

View File

@@ -303,7 +303,7 @@ static void convert_lines_to_vertices(const Slic3r::Lines& lines, const std::vec
static void convert_to_vertices(const Slic3r::ExtrusionPath& extrusion_path, float print_z, size_t layer_id, size_t extruder_id, size_t color_id,
EGCodeExtrusionRole extrusion_role, const Slic3r::Point& shift, std::vector<PathVertex>& vertices)
{
Slic3r::Polyline polyline = extrusion_path.polyline;
Slic3r::Polyline polyline = extrusion_path.polyline.to_polyline();
polyline.remove_duplicate_points();
polyline.translate(shift);
const Slic3r::Lines lines = polyline.lines();
@@ -319,7 +319,7 @@ static void convert_to_vertices(const Slic3r::ExtrusionMultiPath& extrusion_mult
std::vector<float> widths;
std::vector<float> heights;
for (const Slic3r::ExtrusionPath& extrusion_path : extrusion_multi_path.paths) {
Slic3r::Polyline polyline = extrusion_path.polyline;
Slic3r::Polyline polyline = extrusion_path.polyline.to_polyline();
polyline.remove_duplicate_points();
polyline.translate(shift);
const Slic3r::Lines lines_this = polyline.lines();
@@ -337,7 +337,7 @@ static void convert_to_vertices(const Slic3r::ExtrusionLoop& extrusion_loop, flo
std::vector<float> widths;
std::vector<float> heights;
for (const Slic3r::ExtrusionPath& extrusion_path : extrusion_loop.paths) {
Slic3r::Polyline polyline = extrusion_path.polyline;
Slic3r::Polyline polyline = extrusion_path.polyline.to_polyline();
polyline.remove_duplicate_points();
polyline.translate(shift);
const Slic3r::Lines lines_this = polyline.lines();

View File

@@ -1,5 +1,7 @@
#include "MainFrame.hpp"
#include <boost/filesystem/directory.hpp>
#include <boost/filesystem/operations.hpp>
#include <wx/panel.h>
#include <wx/notebook.h>
#include <wx/listbook.h>
@@ -23,6 +25,7 @@
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Utils.hpp"
#include "Tab.hpp"
#include "ProgressStatusBar.hpp"
@@ -2451,6 +2454,17 @@ void MainFrame::init_menubar_as_editor()
open_recent_project(file_id, filename);
}, wxID_FILE1, wxID_FILE1 + 49); // [5050, 5100)
std::vector<std::string> non_planar_projects;
for (auto &&entry : fs::directory_iterator(resources_dir() + "/nonplanar")) {
if (fs::is_regular_file(entry) && entry.path().extension() == ".3mf") {
non_planar_projects.push_back(entry.path().string());
}
}
std::sort(non_planar_projects.begin(), non_planar_projects.end());
for (auto &&path : non_planar_projects) {
m_recent_projects.AddFileToHistory(from_u8(path));
}
std::vector<std::string> recent_projects = wxGetApp().app_config->get_recent_projects();
std::reverse(recent_projects.begin(), recent_projects.end());
for (const std::string& project : recent_projects)

View File

@@ -621,7 +621,7 @@ void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::
Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/)
{
if (!m_config->has(opt_key)) {
std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.\n";
// Option not in config — may be newly added (e.g. ZAA options)
}
std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index);

View File

@@ -3816,7 +3816,7 @@ static std::vector<Search::InputInfo> get_search_inputs(ConfigOptionMode mode)
auto& tabs_list = wxGetApp().tabs_list;
auto print_tech = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
for (auto tab : tabs_list)
if (tab->supports_printer_technology(print_tech))
if (tab && tab->supports_printer_technology(print_tech) && tab->get_config())
ret.emplace_back(Search::InputInfo {tab->get_config(), tab->type(), mode});
return ret;
@@ -3840,7 +3840,9 @@ void Sidebar::update_mode()
//obj_list()->get_sizer()->Show(m_mode > comSimple);
obj_list()->unselect_objects();
obj_list()->update_selections();
// Guard: during startup the 3D canvas selection may not be fully initialized
if (wxGetApp().initialized())
obj_list()->update_selections();
// obj_list()->update_object_menu();
Layout();
@@ -10384,12 +10386,13 @@ void Plater::priv::set_project_name(const wxString& project_name)
{
BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << __LINE__ << " project is:" << project_name;
m_project_name = project_name;
wxString name = project_name + " - OrcaSlicer-ZAA";
//update topbar title
#ifdef __WINDOWS__
wxGetApp().mainframe->SetTitle(m_project_name + " - OrcaSlicer");
wxGetApp().mainframe->topbar()->SetTitle(m_project_name);
wxGetApp().mainframe->SetTitle(name);
wxGetApp().mainframe->topbar()->SetTitle(name);
#else
wxGetApp().mainframe->SetTitle(m_project_name);
wxGetApp().mainframe->SetTitle(name);
if (!m_project_name.IsEmpty())
wxGetApp().mainframe->update_title_colour_after_set_title();
#endif

View File

@@ -70,6 +70,7 @@ static std::string get_key(const std::string &opt_key, Preset::Type type) { retu
void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type type, ConfigOptionMode mode)
{
if (!config) return;
auto emplace = [this, type](const std::string key, const wxString &label) {
const GroupAndCategory &gc = groups_and_categories[key];
if (gc.group.IsEmpty() || gc.category.IsEmpty()) return;
@@ -89,13 +90,19 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty
};
for (std::string opt_key : config->keys()) {
const ConfigOptionDef &opt = config->def()->options.at(opt_key);
auto def_it = config->def()->options.find(opt_key);
if (def_it == config->def()->options.end()) {
continue;
}
const ConfigOptionDef &opt = def_it->second;
if (opt.mode > mode) continue;
int cnt = 0;
if ((type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER) && opt_key != "printable_area")
switch (config->option(opt_key)->type()) {
if ((type == Preset::TYPE_SLA_MATERIAL || type == Preset::TYPE_PRINTER || type == Preset::TYPE_PRINT) && opt_key != "printable_area") {
const ConfigOption *opt_ptr = config->option(opt_key);
if (!opt_ptr) continue;
switch (opt_ptr->type()) {
case coInts: change_opt_key<ConfigOptionInts>(opt_key, config, cnt); break;
case coBools: change_opt_key<ConfigOptionBools>(opt_key, config, cnt); break;
case coFloats: change_opt_key<ConfigOptionFloats>(opt_key, config, cnt); break;
@@ -106,6 +113,7 @@ void OptionsSearcher::append_options(DynamicPrintConfig *config, Preset::Type ty
case coEnums: change_opt_key<ConfigOptionInts>(opt_key, config, cnt); break;
default: break;
}
}
wxString label = opt.full_label.empty() ? opt.label : opt.full_label;
@@ -222,7 +230,7 @@ bool OptionsSearcher::search(const std::string &search, bool force /* = false*/,
if (full_list) {
std::string label = into_u8(get_label(opt));
//all
if (type == Preset::TYPE_INVALID) {
if (type == Preset::TYPE_INVALID) {
found.emplace_back(FoundOption{label, label, into_u8(get_tooltip(opt)), i, 0});
} else if (type == opt.type){
found.emplace_back(FoundOption{label, label, into_u8(get_tooltip(opt)), i, 0});
@@ -289,7 +297,9 @@ OptionsSearcher::~OptionsSearcher() {}
void OptionsSearcher::init(std::vector<InputInfo> input_values)
{
options.clear();
for (auto i : input_values) append_options(i.config, i.type, i.mode);
for (size_t idx = 0; idx < input_values.size(); ++idx) {
append_options(input_values[idx].config, input_values[idx].type, input_values[idx].mode);
}
sort_options();
search(search_line, true, search_type);

View File

@@ -2322,6 +2322,14 @@ void TabPrint::build()
optgroup->append_single_option_line("ironing_angle", "quality_settings_ironing#angle-offset");
optgroup->append_single_option_line("ironing_angle_fixed", "quality_settings_ironing#fixed-angle");
optgroup = page->new_optgroup("Z Contouring", L"param_advanced");
optgroup->append_single_option_line("zaa_enabled");
optgroup->append_single_option_line("zaa_region_disable");
optgroup->append_single_option_line("zaa_minimize_perimeter_height");
optgroup->append_single_option_line("zaa_dont_alternate_fill_direction");
optgroup->append_single_option_line("zaa_min_z");
optgroup->append_single_option_line("ironing_expansion");
optgroup = page->new_optgroup(L("Wall generator"), L"param_wall_generator");
optgroup->append_single_option_line("wall_generator", "quality_settings_wall_generator");
optgroup->append_single_option_line("wall_transition_angle", "quality_settings_wall_generator#arachne");

View File

@@ -8,6 +8,7 @@ if(NOT DEFINED BBL_INTERNAL_TESTING)
set(BBL_INTERNAL_TESTING "0")
endif()
set(SoftFever_VERSION "2.3.2-dev")
set(ZAA_VERSION "1.0.3")
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)"
SoftFever_VERSION_MATCH ${SoftFever_VERSION})
set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1})