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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -43,3 +43,4 @@ test.js
|
||||
/.cache/
|
||||
.clangd
|
||||
internal_docs/
|
||||
resources/nonplanar/
|
||||
|
||||
41
docs/ZAA.md
Normal file
41
docs/ZAA.md
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -100,6 +100,7 @@ set(lisbslic3r_sources
|
||||
CommonDefs.hpp
|
||||
Config.cpp
|
||||
Config.hpp
|
||||
ContourZ.cpp
|
||||
CustomGCode.cpp
|
||||
CustomGCode.hpp
|
||||
CutUtils.cpp
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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))); }
|
||||
|
||||
|
||||
@@ -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
333
src/libslic3r/ContourZ.cpp
Normal 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
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ¢er3 = paths.front().polyline.points.front();
|
||||
pt.rotate(angle, Point(center3.x(), center3.y()));
|
||||
// generate the travel move
|
||||
gcode += m_writer.extrude_to_xy(this->point_to_gcode(pt), 0,"move inwards before travel",true);
|
||||
}
|
||||
@@ -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(®ion - &by_region.front()).config());
|
||||
chain_and_reorder_extrusion_entities(extrusions, &m_last_pos);
|
||||
chain_and_reorder_extrusion_entities(extrusions, m_last_pos.to_point());
|
||||
for (const ExtrusionEntity *fill : extrusions) {
|
||||
auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(fill);
|
||||
if (eec) {
|
||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities)
|
||||
for (ExtrusionEntity *ee : eec->chained_path_from(m_last_pos.to_point()).entities)
|
||||
gcode += this->extrude_entity(*ee, extrusion_name);
|
||||
} else
|
||||
gcode += this->extrude_entity(*fill, extrusion_name);
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()); }
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -471,6 +471,51 @@ bool MultiPoint3::remove_duplicate_points()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Douglas-Peucker simplification for 3D points
|
||||
Points3 MultiPoint3::_douglas_peucker(const Points3 &points, double tolerance)
|
||||
{
|
||||
if (points.size() <= 2) return points;
|
||||
|
||||
// Find the point with maximum distance from line segment
|
||||
double max_dist = 0;
|
||||
size_t max_idx = 0;
|
||||
const Point3 &first = points.front();
|
||||
const Point3 &last = points.back();
|
||||
Line3 line(first, last);
|
||||
|
||||
for (size_t i = 1; i < points.size() - 1; ++i) {
|
||||
// Calculate perpendicular distance to line segment
|
||||
Point3 proj = points[i].projection_onto(line);
|
||||
double dist = points[i].distance_to(proj);
|
||||
if (dist > max_dist) {
|
||||
max_dist = dist;
|
||||
max_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
// If max distance is greater than tolerance, recursively simplify
|
||||
if (max_dist > tolerance) {
|
||||
// Recursive call for first part
|
||||
Points3 left_points(points.begin(), points.begin() + max_idx + 1);
|
||||
Points3 left_result = _douglas_peucker(left_points, tolerance);
|
||||
|
||||
// Recursive call for second part
|
||||
Points3 right_points(points.begin() + max_idx, points.end());
|
||||
Points3 right_result = _douglas_peucker(right_points, tolerance);
|
||||
|
||||
// Concatenate results (avoiding duplicate middle point)
|
||||
Points3 result = left_result;
|
||||
result.insert(result.end(), right_result.begin() + 1, right_result.end());
|
||||
return result;
|
||||
} else {
|
||||
// All points between first and last can be removed
|
||||
Points3 result;
|
||||
result.push_back(first);
|
||||
result.push_back(last);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const MultiPoint &mp)
|
||||
{
|
||||
return BoundingBox(mp.points);
|
||||
@@ -514,4 +559,78 @@ void MultiPoint::symmetric_y(const coord_t &x_axis)
|
||||
}
|
||||
}
|
||||
|
||||
// MultiPoint3 implementations
|
||||
void MultiPoint3::rotate(double cos_angle, double sin_angle)
|
||||
{
|
||||
for (Point3 &pt : this->points) {
|
||||
double cur_x = double(pt(0));
|
||||
double cur_y = double(pt(1));
|
||||
pt(0) = coord_t(round(cos_angle * cur_x - sin_angle * cur_y));
|
||||
pt(1) = coord_t(round(cos_angle * cur_y + sin_angle * cur_x));
|
||||
// Keep Z unchanged
|
||||
}
|
||||
}
|
||||
|
||||
void MultiPoint3::rotate(double angle, const Point3 ¢er)
|
||||
{
|
||||
double s = sin(angle);
|
||||
double c = cos(angle);
|
||||
for (Point3 &pt : points) {
|
||||
Vec3crd v(pt - center);
|
||||
pt(0) = (coord_t)round(double(center(0)) + c * v[0] - s * v[1]);
|
||||
pt(1) = (coord_t)round(double(center(1)) + c * v[1] + s * v[0]);
|
||||
// Keep Z unchanged from original point
|
||||
}
|
||||
}
|
||||
|
||||
int MultiPoint3::find_point(const Point &point) const
|
||||
{
|
||||
for (const Point3 &pt : this->points)
|
||||
if (pt.to_point() == point)
|
||||
return int(&pt - &this->points.front());
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
int MultiPoint3::find_point(const Point &point, double scaled_epsilon) const
|
||||
{
|
||||
if (scaled_epsilon == 0) return this->find_point(point);
|
||||
|
||||
auto dist2_min = std::numeric_limits<double>::max();
|
||||
auto eps2 = scaled_epsilon * scaled_epsilon;
|
||||
int idx_min = -1;
|
||||
for (const Point3 &pt : this->points) {
|
||||
double d2 = (pt.to_point() - point).cast<double>().squaredNorm();
|
||||
if (d2 < dist2_min) {
|
||||
idx_min = int(&pt - &this->points.front());
|
||||
dist2_min = d2;
|
||||
}
|
||||
}
|
||||
return (dist2_min < eps2) ? idx_min : -1;
|
||||
}
|
||||
|
||||
int MultiPoint3::find_point(const Point3 &point) const
|
||||
{
|
||||
for (const Point3 &pt : this->points)
|
||||
if (pt == point)
|
||||
return int(&pt - &this->points.front());
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
int MultiPoint3::find_point(const Point3 &point, double scaled_epsilon) const
|
||||
{
|
||||
if (scaled_epsilon == 0) return this->find_point(point);
|
||||
|
||||
auto dist2_min = std::numeric_limits<double>::max();
|
||||
auto eps2 = scaled_epsilon * scaled_epsilon;
|
||||
int idx_min = -1;
|
||||
for (const Point3 &pt : this->points) {
|
||||
double d2 = (pt - point).cast<double>().squaredNorm();
|
||||
if (d2 < dist2_min) {
|
||||
idx_min = int(&pt - &this->points.front());
|
||||
dist2_min = d2;
|
||||
}
|
||||
}
|
||||
return (dist2_min < eps2) ? idx_min : -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 ¢er);
|
||||
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ¢er) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 ¢er);
|
||||
|
||||
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 ¢er) 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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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})
|
||||
|
||||
Reference in New Issue
Block a user