mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 19:01:02 +00:00
Merge branch 'main' into dev/ams-heat
This commit is contained in:
@@ -1660,7 +1660,7 @@ void SkeletalTrapezoidation::propagateBeadingsDownward(edge_t* edge_to_peak, ptr
|
||||
}
|
||||
|
||||
|
||||
SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius) const
|
||||
SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius)
|
||||
{
|
||||
assert(ratio_left_to_whole >= 0.0 && ratio_left_to_whole <= 1.0);
|
||||
Beading ret = interpolate(left, ratio_left_to_whole, right);
|
||||
@@ -1684,6 +1684,12 @@ SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beadin
|
||||
{ // We cant adjust to fit the next edge because there is no previous one?!
|
||||
return ret;
|
||||
}
|
||||
// ret follows the thicker of left/right, which can hold fewer insets than left when bead
|
||||
// count and thickness disagree; skip the adjustment rather than index ret past its end.
|
||||
if (next_inset_idx >= coord_t(ret.toolpath_locations.size()))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
assert(next_inset_idx < coord_t(left.toolpath_locations.size()));
|
||||
assert(left.toolpath_locations[next_inset_idx] <= switching_radius);
|
||||
assert(left.toolpath_locations[next_inset_idx + 1] >= switching_radius);
|
||||
@@ -1703,7 +1709,7 @@ SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beadin
|
||||
}
|
||||
|
||||
|
||||
SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right) const
|
||||
SkeletalTrapezoidation::Beading SkeletalTrapezoidation::interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right)
|
||||
{
|
||||
assert(ratio_left_to_whole >= 0.0 && ratio_left_to_whole <= 1.0);
|
||||
float ratio_right_to_whole = 1.0 - ratio_left_to_whole;
|
||||
|
||||
@@ -488,7 +488,7 @@ protected:
|
||||
* beads.
|
||||
* \return The beading at the interpolated location.
|
||||
*/
|
||||
Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius) const;
|
||||
static Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right, coord_t switching_radius);
|
||||
|
||||
/*!
|
||||
* Subroutine of \ref interpolate(const Beading&, Ratio, const Beading&, coord_t)
|
||||
@@ -501,7 +501,7 @@ protected:
|
||||
* \param right One of the beadings to interpolate between.
|
||||
* \return The beading at the interpolated location.
|
||||
*/
|
||||
Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right) const;
|
||||
static Beading interpolate(const Beading& left, double ratio_left_to_whole, const Beading& right);
|
||||
|
||||
/*!
|
||||
* Get the beading at a certain node of the skeletal graph, or create one if
|
||||
|
||||
@@ -221,6 +221,7 @@ set(lisbslic3r_sources
|
||||
GCode/FanMover.hpp
|
||||
GCode/GCodeProcessor.cpp
|
||||
GCode/GCodeProcessor.hpp
|
||||
GCode/ElegooGCodeProcessorHelper.cpp
|
||||
GCode.hpp
|
||||
GCode/PchipInterpolatorHelper.cpp
|
||||
GCode/PchipInterpolatorHelper.hpp
|
||||
|
||||
@@ -188,6 +188,18 @@ ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta)
|
||||
return results;
|
||||
}
|
||||
|
||||
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta, Clipper2Lib::JoinType joinType)
|
||||
{
|
||||
Clipper2Lib::Paths64 subject = Slic3rExPolygons_to_Paths64(expolygons);
|
||||
Clipper2Lib::ClipperOffset offsetter;
|
||||
offsetter.AddPaths(subject, joinType, Clipper2Lib::EndType::Polygon);
|
||||
Clipper2Lib::PolyPath64 polytree;
|
||||
offsetter.Execute(delta, polytree);
|
||||
ExPolygons results = PolyTreeToExPolygons(std::move(polytree));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
ExPolygons offset2_ex_2(const ExPolygons& expolygons, double delta1, double delta2)
|
||||
{
|
||||
// 1st offset
|
||||
|
||||
@@ -15,6 +15,7 @@ Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Pol
|
||||
ExPolygons union_ex_2(const Polygons &expolygons);
|
||||
ExPolygons union_ex_2(const ExPolygons &expolygons);
|
||||
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta);
|
||||
ExPolygons offset_ex_2(const ExPolygons &expolygons, double delta, Clipper2Lib::JoinType joinType);
|
||||
ExPolygons offset2_ex_2(const ExPolygons &expolygons, double delta1, double delta2);
|
||||
}
|
||||
|
||||
|
||||
@@ -363,6 +363,7 @@ public:
|
||||
virtual void set_with_restore(const ConfigOptionVectorBase* rhs, std::vector<int>& restore_index, int stride) = 0;
|
||||
virtual void set_with_restore_2(const ConfigOptionVectorBase* rhs, std::vector<int>& restore_index, int start, int len, bool skip_error = false) = 0;
|
||||
virtual void set_only_diff(const ConfigOptionVectorBase* rhs, std::vector<int>& diff_index, int stride) = 0;
|
||||
virtual void set_to_index(const ConfigOptionVectorBase* rhs, std::vector<int>& dest_index, int stride) = 0;
|
||||
virtual void set_with_nil(const ConfigOptionVectorBase* rhs, const ConfigOptionVectorBase* inherits, int stride) = 0;
|
||||
// Resize the vector of values, copy the newly added values from opt_default if provided.
|
||||
virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
|
||||
@@ -586,6 +587,32 @@ public:
|
||||
throw ConfigurationError("ConfigOptionVector::set_only_diff(): Assigning an incompatible type");
|
||||
}
|
||||
|
||||
//set a item related with extruder variants when apply static config with dynamic config
|
||||
//rhs: item from dynamic config
|
||||
//dest_index: which index in this vector need to be used
|
||||
virtual void set_to_index(const ConfigOptionVectorBase* rhs, std::vector<int>& dest_index, int stride) override
|
||||
{
|
||||
if (rhs->type() == this->type()) {
|
||||
// Assign the first value of the rhs vector.
|
||||
auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||
T v = other->values.front();
|
||||
this->values.resize(dest_index.size() * stride, v);
|
||||
|
||||
for (size_t i = 0; i < dest_index.size(); i++) {
|
||||
if (dest_index[i] < 0)
|
||||
continue;
|
||||
for (size_t j = 0; j < size_t(stride); j++)
|
||||
{
|
||||
const size_t src_idx = size_t(dest_index[i]) * size_t(stride) + j;
|
||||
if (src_idx < other->values.size() && !other->is_nil(size_t(dest_index[i]) * size_t(stride)))
|
||||
this->values[i * size_t(stride) + j] = other->values[src_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
throw ConfigurationError("ConfigOptionVector::set_to_index(): Assigning an incompatible type");
|
||||
}
|
||||
|
||||
//set a item related with extruder variants when saving user config, set the non-diff value of some extruder to nill
|
||||
//this item has different value with inherit config
|
||||
//rhs: item from userconfig
|
||||
|
||||
@@ -272,6 +272,10 @@ struct SurfaceFillParams
|
||||
// For Gyroid: when true, use the parameterized "optimized" wave.
|
||||
bool gyroid_optimized = false;
|
||||
|
||||
bool anisotropic_surfaces{false};
|
||||
CenterOfSurfacePattern center_of_surface_pattern{CenterOfSurfacePattern::Each_Surface};
|
||||
bool separated_infills{false};
|
||||
|
||||
bool operator<(const SurfaceFillParams &rhs) const {
|
||||
#define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false;
|
||||
#define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false;
|
||||
@@ -301,8 +305,12 @@ struct SurfaceFillParams
|
||||
RETURN_COMPARE_NON_EQUAL(lateral_lattice_angle_2);
|
||||
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
|
||||
RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
|
||||
RETURN_COMPARE_NON_EQUAL(skin_infill_depth); RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
|
||||
RETURN_COMPARE_NON_EQUAL(skin_infill_depth);
|
||||
RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
|
||||
RETURN_COMPARE_NON_EQUAL(gyroid_optimized);
|
||||
RETURN_COMPARE_NON_EQUAL(anisotropic_surfaces);
|
||||
RETURN_COMPARE_NON_EQUAL(center_of_surface_pattern);
|
||||
RETURN_COMPARE_NON_EQUAL(separated_infills);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -329,6 +337,9 @@ struct SurfaceFillParams
|
||||
this->infill_lock_depth == rhs.infill_lock_depth &&
|
||||
this->skin_infill_depth == rhs.skin_infill_depth &&
|
||||
this->infill_overhang_angle == rhs.infill_overhang_angle &&
|
||||
this->anisotropic_surfaces == rhs.anisotropic_surfaces &&
|
||||
this->center_of_surface_pattern == rhs.center_of_surface_pattern &&
|
||||
this->separated_infills == rhs.separated_infills &&
|
||||
this->gyroid_optimized == rhs.gyroid_optimized;
|
||||
}
|
||||
};
|
||||
@@ -868,6 +879,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
params.lateral_lattice_angle_1 = region_config.lateral_lattice_angle_1;
|
||||
params.lateral_lattice_angle_2 = region_config.lateral_lattice_angle_2;
|
||||
params.infill_overhang_angle = region_config.infill_overhang_angle;
|
||||
params.anisotropic_surfaces = region_config.anisotropic_surfaces;
|
||||
params.center_of_surface_pattern = region_config.center_of_surface_pattern;
|
||||
params.separated_infills = region_config.separated_infills;
|
||||
if (params.pattern == ipLockedZag) {
|
||||
params.infill_lock_depth = scale_(region_config.infill_lock_depth);
|
||||
params.skin_infill_depth = scale_(region_config.skin_infill_depth);
|
||||
@@ -1309,6 +1323,22 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
params.config = ®ion_config;
|
||||
params.pattern = surface_fill.params.pattern;
|
||||
|
||||
// Orca: Checking the filling of a centered surface by drawing for each model parts
|
||||
bool is_top_or_bottom = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface;
|
||||
bool is_centered_infill = surface_fill.params.pattern == ipArchimedeanChords || surface_fill.params.pattern == ipOctagramSpiral;
|
||||
if (is_top_or_bottom) {
|
||||
params.is_anisotropic = surface_fill.params.anisotropic_surfaces; // Orca: anisotropic surfaces
|
||||
params.center_of_surface_pattern = surface_fill.params.center_of_surface_pattern; // Orca: center of surface pattern
|
||||
}
|
||||
// Orca: Each_Model centers the pattern on each model part's bbox; Each_Surface / Each_Assembly
|
||||
// fall through to the default (whole-object) bounding box below.
|
||||
bool is_per_model_center = is_top_or_bottom && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model && is_centered_infill;
|
||||
bool is_separate_infill = !is_top_or_bottom && surface_fill.params.separated_infills &&
|
||||
(
|
||||
is_separable_infill_pattern(surface_fill.params.pattern) ||
|
||||
params.config->solid_infill_rotate_template != "" ||
|
||||
params.config->sparse_infill_rotate_template != "" );
|
||||
|
||||
if( surface_fill.params.pattern == ipLockedZag ) {
|
||||
params.locked_zag = true;
|
||||
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
|
||||
@@ -1332,7 +1362,36 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
params.can_reverse = false;
|
||||
for (ExPolygon& expoly : surface_fill.expolygons) {
|
||||
|
||||
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
|
||||
// Orca: separate infill / per-model pattern centering.
|
||||
//
|
||||
// Center the pattern on each connected body of the object independently, so every piece
|
||||
// is filled exactly as if it were sliced on its own: touching/overlapping parts merge
|
||||
// into one body sharing a center, while separate parts and disconnected islands (even
|
||||
// interleaved-but-not-touching ones, e.g. chain links) each get their own. The body each
|
||||
// island belongs to, and its full bounding box, were resolved in 3D by PrintObject::
|
||||
// infill() (lslices_separated_component_bboxes, aligned with this layer's lslices). We
|
||||
// match this fill region to the island it overlaps most, then re-use the whole-object
|
||||
// bounding box (origin-centered — identical extent to the default, so coverage and cost
|
||||
// are unchanged) re-centered on that body.
|
||||
if (is_per_model_center || is_separate_infill) {
|
||||
double best_overlap = 0.;
|
||||
BoundingBox best_component;
|
||||
for (size_t r = 0; r < this->lslices.size() && r < this->lslices_separated_component_bboxes.size(); ++ r) {
|
||||
const double overlap = area(intersection_ex(this->lslices[r], expoly));
|
||||
if (overlap > best_overlap) {
|
||||
best_overlap = overlap;
|
||||
best_component = this->lslices_separated_component_bboxes[r];
|
||||
}
|
||||
}
|
||||
if (best_component.defined) {
|
||||
const Point c = best_component.center();
|
||||
BoundingBox part_bbox = bbox; // origin-centered, whole-object extent (from above)
|
||||
part_bbox.translate(c.x(), c.y()); // re-center on this body
|
||||
f->set_bounding_box(part_bbox);
|
||||
}
|
||||
} // - End: separate infill / per-model pattern centering
|
||||
|
||||
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
|
||||
if (params.symmetric_infill_y_axis) {
|
||||
params.symmetric_y_axis = f->extended_object_bounding_box().center().x();
|
||||
expoly.symmetric_y(params.symmetric_y_axis);
|
||||
|
||||
@@ -165,7 +165,7 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
|
||||
// ORCA: special flag for flow rate calibration
|
||||
auto is_flow_calib = params.extrusion_role == erTopSolidInfill && this->print_object_config->has("calib_flowrate_topinfill_special_order") &&
|
||||
this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool();
|
||||
if (is_flow_calib) {
|
||||
if (is_flow_calib || params.is_anisotropic) { // Orca: disable sorting while anisotropic surfaces
|
||||
eec->no_sort = true;
|
||||
}
|
||||
size_t idx = eec->entities.size();
|
||||
@@ -186,7 +186,8 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
|
||||
}
|
||||
|
||||
// Orca: run gap fill
|
||||
this->_create_gap_fill(surface, params, eec);
|
||||
if (!(params.is_anisotropic)) // Orca: Disable gap filling while anisotropic
|
||||
this->_create_gap_fill(surface, params, eec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,8 @@ struct FillParams
|
||||
bool locked_zag{false};
|
||||
float infill_lock_depth{0.0};
|
||||
float skin_infill_depth{0.0};
|
||||
bool is_anisotropic{false};
|
||||
CenterOfSurfacePattern center_of_surface_pattern{CenterOfSurfacePattern::Each_Surface};
|
||||
};
|
||||
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
||||
|
||||
|
||||
@@ -77,20 +77,24 @@ void FillPlanePath::_fill_surface_single(
|
||||
|
||||
//FIXME Vojtech: We are not sure whether the user expects the fill patterns on visible surfaces to be aligned across all the islands of a single layer.
|
||||
// One may align for this->centered() to align the patterns for Archimedean Chords and Octagram Spiral patterns.
|
||||
const bool align = params.density < 0.995;
|
||||
|
||||
// Orca: the old implementation became obsolete when it became possible to change the density of the top and bottom surfaces
|
||||
bool align = params.extrusion_role == ExtrusionRole::erInternalInfill;
|
||||
BoundingBox bounding_box;
|
||||
BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON);
|
||||
|
||||
// Expand the bounding box to avoid artifacts at the edges
|
||||
snug_bounding_box.offset(scale_(this->spacing)*params.multiline);
|
||||
snug_bounding_box.offset(scale_(this->spacing)*params.multiline);
|
||||
|
||||
// Rotated bounding box of the area to fill in with the pattern.
|
||||
BoundingBox bounding_box = align ?
|
||||
// Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box.
|
||||
this->bounding_box.rotated(-direction.first) :
|
||||
// Solid infill does not need to be aligned across layers, generate the infill pattern
|
||||
// around the clipping expolygon only.
|
||||
snug_bounding_box;
|
||||
// Sparse infill (or Internal where align == true) needs to be aligned across layers. Align infill across layers using the object's bounding box.
|
||||
// Solid infill does not need to be aligned across layers, generate the infill pattern around the clipping expolygon only.
|
||||
if (align)
|
||||
bounding_box = this->bounding_box.rotated(-direction.first);
|
||||
else if (params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Surface)
|
||||
bounding_box = snug_bounding_box;
|
||||
else if (params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model)
|
||||
bounding_box = this->bounding_box.rotated(-direction.first);
|
||||
else
|
||||
bounding_box = extended_object_bounding_box();
|
||||
|
||||
Point shift = this->centered() ?
|
||||
bounding_box.center() :
|
||||
@@ -129,35 +133,49 @@ void FillPlanePath::_fill_surface_single(
|
||||
polylines = intersection_pl(std::move(polylines), expolygon);
|
||||
if (!polylines.empty()) {
|
||||
Polylines chained;
|
||||
if (params.dont_connect() || params.density > 0.5) {
|
||||
// ORCA: special flag for flow rate calibration
|
||||
auto is_flow_calib = params.extrusion_role == erTopSolidInfill &&
|
||||
this->print_object_config->has("calib_flowrate_topinfill_special_order") &&
|
||||
this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool() &&
|
||||
dynamic_cast<FillArchimedeanChords*>(this);
|
||||
if (is_flow_calib) {
|
||||
// We want the spiral part to be printed inside-out
|
||||
// Find the center spiral line first, by looking for the longest one
|
||||
auto it = std::max_element(polylines.begin(), polylines.end(),
|
||||
[](const Polyline& a, const Polyline& b) { return a.length() < b.length(); });
|
||||
Polyline center_spiral = std::move(*it);
|
||||
if (!params.is_anisotropic) { // Orca: not anisotropic surface
|
||||
if ((params.dont_connect() || params.density > 0.5)) {
|
||||
// ORCA: special flag for flow rate calibration
|
||||
auto is_flow_calib = params.extrusion_role == erTopSolidInfill &&
|
||||
this->print_object_config->has("calib_flowrate_topinfill_special_order") &&
|
||||
this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool() &&
|
||||
dynamic_cast<FillArchimedeanChords*>(this);
|
||||
if (is_flow_calib) {
|
||||
// We want the spiral part to be printed inside-out
|
||||
// Find the center spiral line first, by looking for the longest one
|
||||
auto it = std::max_element(polylines.begin(), polylines.end(),
|
||||
[](const Polyline& a, const Polyline& b) { return a.length() < b.length(); });
|
||||
Polyline center_spiral = std::move(*it);
|
||||
|
||||
// Ensure the spiral is printed from inside to out
|
||||
if (center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm()) {
|
||||
center_spiral.reverse();
|
||||
// Ensure the spiral is printed from inside to out
|
||||
if ((center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm())) {
|
||||
center_spiral.reverse();
|
||||
}
|
||||
|
||||
// Chain the other polylines
|
||||
polylines.erase(it);
|
||||
chained = chain_polylines(std::move(polylines), nullptr);
|
||||
|
||||
// Then add the center spiral back
|
||||
chained.push_back(std::move(center_spiral));
|
||||
} else {
|
||||
chained = chain_polylines(std::move(polylines), nullptr);
|
||||
}
|
||||
|
||||
// Chain the other polylines
|
||||
polylines.erase(it);
|
||||
chained = chain_polylines(std::move(polylines));
|
||||
|
||||
// Then add the center spiral back
|
||||
chained.push_back(std::move(center_spiral));
|
||||
} else {
|
||||
chained = chain_polylines(std::move(polylines));
|
||||
} else
|
||||
connect_infill(std::move(polylines), expolygon, chained, this->spacing, params);
|
||||
} else { // Orca: anisotropic surface
|
||||
const Point _center(0., 0.);
|
||||
for (Polyline& segment : polylines) { // sort paths by its direction
|
||||
if (segment.size() > 1) { // need at least two points to evaluate direction
|
||||
if (segment.first_point().ccw(segment.points[1], _center) < 0)
|
||||
segment.reverse();
|
||||
}
|
||||
chained.emplace_back(std::move(segment));
|
||||
}
|
||||
} else
|
||||
connect_infill(std::move(polylines), expolygon, chained, this->spacing, params);
|
||||
std::sort(chained.begin(), chained.end(), [&_center](const Polyline& a, const Polyline& b) { // just sort polylines from center to outside
|
||||
return a.distance_to(_center) < b.distance_to(_center);
|
||||
});
|
||||
}
|
||||
// paths must be repositioned and rotated back
|
||||
for (Polyline& pl : chained) {
|
||||
pl.translate(shift.x(), shift.y());
|
||||
|
||||
@@ -2739,13 +2739,19 @@ static void polylines_from_paths(const std::vector<MonotonicRegionLink> &path, c
|
||||
|
||||
// The extended bounding box of the whole object that covers any rotation of every layer.
|
||||
BoundingBox FillRectilinear::extended_object_bounding_box() const {
|
||||
// Build the extension around the box center. The transpose merge and the sqrt(2.) scaling
|
||||
// (which covers any possible rotation) are both defined about the origin, so a box that is not
|
||||
// origin-centered — e.g. a separated-infill box re-centered on a single assembly part — would be
|
||||
// distorted. Shift to the origin first and back afterwards; for the default origin-centered box
|
||||
// the two translations cancel and this is identical to the original behavior.
|
||||
const Point c = this->bounding_box.center();
|
||||
BoundingBox out = this->bounding_box;
|
||||
out.translate(-c.x(), -c.y());
|
||||
out.merge(Point(out.min.y(), out.min.x()));
|
||||
out.merge(Point(out.max.y(), out.max.x()));
|
||||
|
||||
// The bounding box is scaled by sqrt(2.) to ensure that the bounding box
|
||||
// covers any possible rotations.
|
||||
return out.scaled(sqrt(2.));
|
||||
out = out.scaled(sqrt(2.));
|
||||
out.translate(c.x(), c.y());
|
||||
return out;
|
||||
}
|
||||
|
||||
bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out)
|
||||
@@ -3098,8 +3104,11 @@ bool FillRectilinear::fill_surface_trapezoidal(
|
||||
|
||||
const coord_t d2 = coord_t(0.5 * period - d1);
|
||||
|
||||
// Align bounding box to the grid
|
||||
bb.merge(align_to_grid(bb.min, Point(period, period)));
|
||||
// Align bounding box to the grid, phased through the box center so separated infills align
|
||||
// each part on itself (grid_center is the origin for a standalone object / feature off).
|
||||
// Captured before the merge, which grows bb and would otherwise shift its center.
|
||||
const Point grid_center = bb.center();
|
||||
bb.merge(align_to_grid(bb.min, Point(period, period), grid_center));
|
||||
const coord_t xmin = bb.min.x();
|
||||
const coord_t xmax = bb.max.x();
|
||||
const coord_t ymin = bb.min.y();
|
||||
@@ -3146,11 +3155,17 @@ bool FillRectilinear::fill_surface_trapezoidal(
|
||||
flip_vertical = !flip_vertical;
|
||||
}
|
||||
|
||||
// transpose points for odd infill layers (taking infill combination into account)
|
||||
// transpose points for odd infill layers (taking infill combination into account).
|
||||
// Orca: mirror across the diagonal through grid_center (not the origin), so the swapped
|
||||
// layers stay aligned with the center-phased grid. For a standalone object / feature off,
|
||||
// grid_center is the origin and this is a plain x/y swap.
|
||||
if (infill_layer_id % 2 == 1) {
|
||||
for (Polyline& pl : polylines) {
|
||||
for (Point& p : pl.points) {
|
||||
std::swap(p.x(), p.y());
|
||||
const coord_t dx = p.x() - grid_center.x();
|
||||
const coord_t dy = p.y() - grid_center.y();
|
||||
p.x() = grid_center.x() + dy;
|
||||
p.y() = grid_center.y() + dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3341,6 +3356,14 @@ bool FillRectilinear::fill_surface_trapezoidal(
|
||||
break;
|
||||
}
|
||||
|
||||
// Orca: cases 1 & 2 build the pattern symmetrically around the origin, so on their own they
|
||||
// phase to the global origin and every part shares one grid. Shift the pattern onto the box
|
||||
// center this->bounding_box carries, so separated infills align each part on itself. The center
|
||||
// is the origin for a standalone object (or when the feature is off), making this a no-op there.
|
||||
if (Pattern_type != 0)
|
||||
for (Polyline &pl : polylines)
|
||||
pl.translate(rotate_vector.second);
|
||||
|
||||
// Apply multiline fill
|
||||
multiline_fill(polylines, params, spacing);
|
||||
|
||||
|
||||
@@ -5949,7 +5949,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
m_thumbnail_middle = iter->second;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
std::string filename = std::string(store_params.path);
|
||||
std::string filename = store_params.path;
|
||||
boost::filesystem::remove(filename + ".tmp", ec);
|
||||
|
||||
bool result = _save_model_to_file(filename + ".tmp", *store_params.model, store_params.plate_data_list, store_params.project_presets, store_params.config,
|
||||
@@ -8988,7 +8988,7 @@ bool store_bbs_3mf(StoreParams& store_params)
|
||||
// All export should use "C" locales for number formatting.
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
||||
if (store_params.path == nullptr || store_params.model == nullptr)
|
||||
if (store_params.path.empty() || store_params.model == nullptr)
|
||||
return false;
|
||||
|
||||
_BBS_3MF_Exporter exporter;
|
||||
|
||||
@@ -226,7 +226,7 @@ typedef std::map<int, PlateData*> PlateDataMaps;
|
||||
|
||||
struct StoreParams
|
||||
{
|
||||
const char* path;
|
||||
std::string path;
|
||||
Model* model = nullptr;
|
||||
PlateDataPtrs plate_data_list;
|
||||
int export_plate_idx = -1;
|
||||
|
||||
@@ -6215,7 +6215,8 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
|
||||
static constexpr const char* support_transition_label = "support transition";
|
||||
static constexpr const char* support_ironing_label = "support ironing";
|
||||
|
||||
static const auto speed_for_path = [&](double length, ExtrusionRole role, double default_speed = -1.0) {
|
||||
// Not static: it captures `this` by reference.
|
||||
const auto speed_for_path = [&](double length, ExtrusionRole role, double default_speed = -1.0) {
|
||||
if (!is_support(role) || length > SMALL_PERIMETER_LENGTH(NOZZLE_CONFIG(small_support_perimeter_threshold)))
|
||||
return default_speed;
|
||||
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
#include "GCodeProcessor.hpp"
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <string_view>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace {
|
||||
|
||||
bool equals_case_insensitive(std::string_view lhs, std::string_view rhs)
|
||||
{
|
||||
return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin(), [](unsigned char l, unsigned char r) {
|
||||
return std::tolower(l) == std::tolower(r);
|
||||
});
|
||||
}
|
||||
|
||||
float get_clamped_param(const GCodeReader::GCodeLine& line, char axis, float default_value, float min_value, float max_value)
|
||||
{
|
||||
float value = default_value;
|
||||
line.has_value(axis, value);
|
||||
return std::clamp(value, min_value, max_value);
|
||||
}
|
||||
|
||||
float extrusion_time(float e_length, float feedrate)
|
||||
{
|
||||
return feedrate > 0.0f && e_length > 0.0f ? e_length / feedrate * 60.0f : 0.0f;
|
||||
}
|
||||
|
||||
float retract_time(float e_length)
|
||||
{
|
||||
static constexpr float retract_feedrate = 1800.0f;
|
||||
return extrusion_time(std::max(e_length, 0.0f), retract_feedrate);
|
||||
}
|
||||
|
||||
float s819_time(float e_length, float feedrate)
|
||||
{
|
||||
static constexpr float s819_tail_flush_length = 10.0f;
|
||||
static constexpr float s819_tail_feedrate = 400.0f;
|
||||
|
||||
const float tail_length = std::min(std::max(e_length, 0.0f), s819_tail_flush_length);
|
||||
const float main_length = std::max(e_length - tail_length, 0.0f);
|
||||
return extrusion_time(main_length, feedrate) + extrusion_time(tail_length, s819_tail_feedrate);
|
||||
}
|
||||
|
||||
float estimate_M6211_time_for_centauri_carbon(const GCodeReader::GCodeLine& line, float length, double current_x,
|
||||
double current_y)
|
||||
{
|
||||
static constexpr float max_segment_length = 73.0f;
|
||||
static constexpr float wipe_after_flush_time = 2.8f;
|
||||
static constexpr float main_feedrate = 500.0f;
|
||||
static constexpr float tail_feedrate = 400.0f;
|
||||
static constexpr float travel_feedrate = 5000.0f;
|
||||
static constexpr double parking_x = 256.0;
|
||||
static constexpr double parking_y = 0.0;
|
||||
|
||||
const float flush_length = std::clamp(length, 10.0f, 1000.0f);
|
||||
const float cool_time = get_clamped_param(line, 'P', 5000.0f, 0.0f, 20000.0f) * 0.001f;
|
||||
const float travel_time = static_cast<float>(std::abs(current_y - parking_y) + std::abs(current_x - parking_x)) /
|
||||
travel_feedrate * 60.0f;
|
||||
|
||||
// Initial time, including: material change, heating, etc.
|
||||
float m6211_time = 18.2f + travel_time;
|
||||
|
||||
float remaining_flush_length = std::max(flush_length, 0.0f);
|
||||
while (remaining_flush_length > 0.0f) {
|
||||
const float segment_length = std::min(remaining_flush_length, max_segment_length);
|
||||
remaining_flush_length -= segment_length;
|
||||
if (segment_length >= max_segment_length) {
|
||||
// Full segment: 3-phase extrusion (30+35+10=75mm) + retract
|
||||
m6211_time += extrusion_time(30.0f, main_feedrate) + extrusion_time(35.0f, main_feedrate) +
|
||||
extrusion_time(10.0f, tail_feedrate) + extrusion_time(2.0f, tail_feedrate) + cool_time +
|
||||
wipe_after_flush_time;
|
||||
} else {
|
||||
// Partial last segment: simple extrude at F500 + retract at F400
|
||||
m6211_time += extrusion_time(segment_length, main_feedrate) + extrusion_time(2.0f, tail_feedrate) + cool_time +
|
||||
wipe_after_flush_time;
|
||||
}
|
||||
}
|
||||
|
||||
return m6211_time;
|
||||
}
|
||||
|
||||
float estimate_M6211_time_for_centauri_carbon_2(const GCodeReader::GCodeLine& line, float length, float new_extruder_temp)
|
||||
{
|
||||
const float flush_length = std::clamp(length, 10.0f, 1000.0f);
|
||||
const float flush_length_single = get_clamped_param(line, 'K', 75.0f, 10.0f, 300.0f);
|
||||
const float old_filament_e_feedrate = get_clamped_param(line, 'M', 300.0f, 10.0f, 600.0f);
|
||||
const float new_filament_e_feedrate = get_clamped_param(line, 'N', 300.0f, 10.0f, 600.0f);
|
||||
const float cool_time = get_clamped_param(line, 'P', 3000.0f, 0.0f, 20000.0f) * 0.001f;
|
||||
|
||||
// The flush length of the old material, unit: mm
|
||||
static constexpr float e_flush_dist = 15.0f;
|
||||
// Wipe time after flush, in seconds
|
||||
static constexpr float wipe_after_flush_time = 5.0f;
|
||||
|
||||
const float flush_length_after_start = std::max(flush_length - e_flush_dist, 0.0f);
|
||||
const int flush_times = std::max(1, static_cast<int>(std::ceil(flush_length_after_start / flush_length_single)));
|
||||
const float flush_length_actual = flush_length_single;
|
||||
|
||||
// Initial time, including: material change, heating, moving, etc.
|
||||
float m6211_time = 31.0f;
|
||||
m6211_time += extrusion_time(std::min(e_flush_dist, flush_length), old_filament_e_feedrate);
|
||||
|
||||
const int intermediate_flush_times = flush_times - 1;
|
||||
const float intermediate_flush_time = s819_time(flush_length_actual, new_filament_e_feedrate) + retract_time(6.0f) + cool_time +
|
||||
wipe_after_flush_time;
|
||||
m6211_time += static_cast<float>(intermediate_flush_times) * intermediate_flush_time;
|
||||
m6211_time += s819_time(flush_length_actual, new_filament_e_feedrate * 0.8f) + retract_time(4.0f) + cool_time + wipe_after_flush_time;
|
||||
|
||||
static constexpr float cooling_rate = 1.36f;
|
||||
const float r_temp = get_clamped_param(line, 'R', new_extruder_temp + 20.0f, 185.0f, 350.0f);
|
||||
const float s_temp = get_clamped_param(line, 'S', 250.0f, 185.0f, 350.0f);
|
||||
|
||||
if (s_temp < r_temp)
|
||||
m6211_time += (r_temp - s_temp) / cooling_rate;
|
||||
|
||||
return m6211_time;
|
||||
}
|
||||
|
||||
float estimate_M6211_time(const GCodeReader::GCodeLine& line, std::string_view printer_model, float length, float new_extruder_temp, double current_x, double current_y)
|
||||
{
|
||||
if (equals_case_insensitive(printer_model, "Elegoo Centauri Carbon") || equals_case_insensitive(printer_model, "Elegoo Centauri")) {
|
||||
return estimate_M6211_time_for_centauri_carbon(line, length, current_x, current_y);
|
||||
} else if (equals_case_insensitive(printer_model, "Elegoo Centauri Carbon 2") ||
|
||||
equals_case_insensitive(printer_model, "Elegoo Centauri 2")) {
|
||||
return estimate_M6211_time_for_centauri_carbon_2(line, length, new_extruder_temp);
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void GCodeProcessor::process_elegoo_M6211(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
float length = 0.0f;
|
||||
if (!line.has_value('L', length) || length <= 0.0f)
|
||||
return;
|
||||
|
||||
float t = -1.0f;
|
||||
if (!line.has_value('T', t) || t < 0.0f)
|
||||
return;
|
||||
|
||||
const int filament_id = static_cast<int>(std::round(t));
|
||||
if (filament_id < 0 || filament_id >= m_result.filaments_count)
|
||||
return;
|
||||
|
||||
const int extruder_id = m_filament_maps[filament_id];
|
||||
|
||||
float new_extruder_temp = 0.0f;
|
||||
if (line.has_value('S', new_extruder_temp)) {
|
||||
if (extruder_id >= 0 && static_cast<size_t>(extruder_id) < m_extruder_temps.size())
|
||||
m_extruder_temps[static_cast<size_t>(extruder_id)] = new_extruder_temp;
|
||||
}
|
||||
|
||||
const float m6211_time = estimate_M6211_time(line, m_printer_model, length, new_extruder_temp,
|
||||
m_start_position[X], m_start_position[Y]);
|
||||
const int curr_filament_id = get_filament_id(false);
|
||||
const bool is_first_extrusion = (curr_filament_id == -1) || (filament_id == curr_filament_id);
|
||||
|
||||
m_time_processor.filament_unload_times = 0;
|
||||
m_time_processor.filament_load_times = m6211_time;
|
||||
process_filament_change(filament_id);
|
||||
|
||||
if (extruder_id >= 0 && static_cast<size_t>(extruder_id) < m_remaining_volume.size()) {
|
||||
const float remaining_volume = static_cast<size_t>(extruder_id) < m_nozzle_volume.size() ?
|
||||
m_nozzle_volume[extruder_id] :
|
||||
0.0f;
|
||||
const float filament_diameter = static_cast<size_t>(filament_id) < m_result.filament_diameters.size() ?
|
||||
m_result.filament_diameters[filament_id] :
|
||||
m_result.filament_diameters.back();
|
||||
const float area_filament_cross_section = static_cast<float>(M_PI) * sqr(0.5f * filament_diameter);
|
||||
const float volume_flushed_filament = area_filament_cross_section * length;
|
||||
|
||||
if (volume_flushed_filament >= remaining_volume) {
|
||||
if (!is_first_extrusion)
|
||||
m_used_filaments.update_flush_per_filament(curr_filament_id, remaining_volume);
|
||||
|
||||
m_used_filaments.update_flush_per_filament(filament_id, volume_flushed_filament - remaining_volume);
|
||||
m_remaining_volume[extruder_id] = 0.0f;
|
||||
} else {
|
||||
m_used_filaments.update_flush_per_filament(filament_id, volume_flushed_filament);
|
||||
m_remaining_volume[extruder_id] -= volume_flushed_filament;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1839,7 +1839,8 @@ void GCodeProcessor::register_commands()
|
||||
{"VM104", [this](const GCodeReader::GCodeLine& line) { process_VM104(line); }},
|
||||
{"VM109", [this](const GCodeReader::GCodeLine& line) { process_VM109(line); }},
|
||||
{"M622", [this](const GCodeReader::GCodeLine& line) { process_M622(line);}},
|
||||
{"M623", [this](const GCodeReader::GCodeLine& line) { process_M623(line);}}
|
||||
{"M623", [this](const GCodeReader::GCodeLine& line) { process_M623(line);}},
|
||||
{"M6211", [this](const GCodeReader::GCodeLine& line) { process_M6211(line); }}
|
||||
};
|
||||
|
||||
std::unordered_set<std::string>early_quit_commands = {
|
||||
@@ -1863,6 +1864,12 @@ void GCodeProcessor::register_commands()
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_M6211(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (boost::algorithm::istarts_with(m_printer_model, "elegoo"))
|
||||
process_elegoo_M6211(line);
|
||||
}
|
||||
|
||||
bool GCodeProcessor::check_multi_extruder_gcode_valid(const int extruder_size,
|
||||
const Pointfs plate_printable_area,
|
||||
const double plate_printable_height,
|
||||
@@ -2027,6 +2034,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
m_parser.apply_config(config);
|
||||
|
||||
m_flavor = config.gcode_flavor;
|
||||
m_printer_model = config.printer_model.value;
|
||||
|
||||
m_single_extruder_multi_material = config.single_extruder_multi_material;
|
||||
|
||||
@@ -2213,6 +2221,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
if (printer_settings_id != nullptr)
|
||||
m_result.settings_ids.printer = printer_settings_id->value;
|
||||
|
||||
const ConfigOptionString* printer_model = config.option<ConfigOptionString>("printer_model");
|
||||
if (printer_model != nullptr)
|
||||
m_printer_model = printer_model->value;
|
||||
|
||||
// BBS
|
||||
m_result.filaments_count = config.option<ConfigOptionFloats>("filament_diameter")->values.size();
|
||||
|
||||
|
||||
@@ -846,6 +846,7 @@ class Print;
|
||||
float m_preheat_time;
|
||||
int m_preheat_steps;
|
||||
bool m_disable_m73;
|
||||
std::string m_printer_model;
|
||||
|
||||
enum class EProducer
|
||||
{
|
||||
@@ -1084,6 +1085,10 @@ class Print;
|
||||
// Unload the current filament into the MK3 MMU2 unit at the end of print.
|
||||
void process_M702(const GCodeReader::GCodeLine& line);
|
||||
|
||||
//Used for Elegoo printer to change tool head
|
||||
void process_M6211(const GCodeReader::GCodeLine& line);
|
||||
void process_elegoo_M6211(const GCodeReader::GCodeLine& line);
|
||||
|
||||
void process_SYNC(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Processes T line (Select Tool)
|
||||
|
||||
@@ -138,7 +138,8 @@ static double calc_max_layer_height(const PrintConfig &config, double max_object
|
||||
{
|
||||
double max_layer_height = std::numeric_limits<double>::max();
|
||||
for (size_t i = 0; i < config.nozzle_diameter.values.size(); ++ i) {
|
||||
double mlh = config.max_layer_height.values[i];
|
||||
// max_layer_height may be shorter than the extruder count; get_at() clamps.
|
||||
double mlh = config.max_layer_height.get_at(i);
|
||||
if (mlh == 0.)
|
||||
mlh = 0.75 * config.nozzle_diameter.values[i];
|
||||
max_layer_height = std::min(max_layer_height, mlh);
|
||||
@@ -187,6 +188,10 @@ static void apply_first_layer_order(const DynamicPrintConfig* config, std::vecto
|
||||
|
||||
void ToolOrdering::handle_dontcare_extruder(const std::vector<unsigned int>& tool_order_layer0)
|
||||
{
|
||||
const PrintConfig* print_config = m_print_config_ptr;
|
||||
if (!print_config && m_print_object_ptr)
|
||||
print_config = &m_print_object_ptr->print()->config();
|
||||
|
||||
if(m_layer_tools.empty() || tool_order_layer0.empty())
|
||||
return;
|
||||
|
||||
@@ -221,6 +226,8 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector<unsigned int>& too
|
||||
for (int i = 1; i < m_layer_tools.size(); i++) {
|
||||
LayerTools& lt = m_layer_tools[i];
|
||||
|
||||
// Extruders in lt.extruders are already sorted.
|
||||
|
||||
if (lt.extruders.empty())
|
||||
continue;
|
||||
if (lt.extruders.size() == 1 && lt.extruders.front() == 0)
|
||||
@@ -229,14 +236,23 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector<unsigned int>& too
|
||||
if (lt.extruders.front() == 0)
|
||||
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
|
||||
lt.extruders.erase(lt.extruders.begin());
|
||||
// Reorder the extruders to start with the last one.
|
||||
for (size_t i = 1; i < lt.extruders.size(); ++i)
|
||||
if (lt.extruders[i] == last_extruder_id) {
|
||||
// Move the last extruder to the front.
|
||||
memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int));
|
||||
lt.extruders.front() = last_extruder_id;
|
||||
break;
|
||||
|
||||
if (print_config == nullptr
|
||||
|| print_config->toolchange_ordering == ToolChangeOrderingType::Default)
|
||||
{
|
||||
// Reorder the extruders to start with the last one.
|
||||
for (size_t i = 1; i < lt.extruders.size(); ++i) {
|
||||
if (lt.extruders[i] == last_extruder_id) {
|
||||
// Move the last extruder to the front.
|
||||
std::rotate(
|
||||
lt.extruders.begin(),
|
||||
lt.extruders.begin() + i,
|
||||
lt.extruders.begin() + i + 1
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
last_extruder_id = lt.extruders.back();
|
||||
}
|
||||
@@ -252,6 +268,10 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector<unsigned int>& too
|
||||
|
||||
void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id)
|
||||
{
|
||||
const PrintConfig* print_config = m_print_config_ptr;
|
||||
if (!print_config && m_print_object_ptr)
|
||||
print_config = &m_print_object_ptr->print()->config();
|
||||
|
||||
if(m_layer_tools.empty())
|
||||
return;
|
||||
if(last_extruder_id == (unsigned int)-1){
|
||||
@@ -275,6 +295,8 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id)
|
||||
}
|
||||
|
||||
for (LayerTools < : m_layer_tools) {
|
||||
// Extruders in lt.extruders are already sorted.
|
||||
|
||||
if (lt.extruders.empty())
|
||||
continue;
|
||||
if (lt.extruders.size() == 1 && lt.extruders.front() == 0)
|
||||
@@ -283,21 +305,30 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id)
|
||||
if (lt.extruders.front() == 0)
|
||||
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
|
||||
lt.extruders.erase(lt.extruders.begin());
|
||||
// Reorder the extruders to start with the last one.
|
||||
for (size_t i = 1; i < lt.extruders.size(); ++ i)
|
||||
if (lt.extruders[i] == last_extruder_id) {
|
||||
// Move the last extruder to the front.
|
||||
memmove(lt.extruders.data() + 1, lt.extruders.data(), i * sizeof(unsigned int));
|
||||
lt.extruders.front() = last_extruder_id;
|
||||
break;
|
||||
|
||||
if (print_config == nullptr
|
||||
|| print_config->toolchange_ordering == ToolChangeOrderingType::Default)
|
||||
{
|
||||
// Reorder the extruders to start with the last one.
|
||||
for (size_t i = 1; i < lt.extruders.size(); ++i) {
|
||||
if (lt.extruders[i] == last_extruder_id) {
|
||||
// Move the last extruder to the front.
|
||||
std::rotate(
|
||||
lt.extruders.begin(),
|
||||
lt.extruders.begin() + i,
|
||||
lt.extruders.begin() + i + 1
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lt == m_layer_tools[0]) {
|
||||
// On first layer with wipe tower, prefer a soluble extruder
|
||||
// at the beginning, so it is not wiped on the first layer.
|
||||
if (m_print_config_ptr && m_print_config_ptr->enable_prime_tower) {
|
||||
if (print_config && print_config->enable_prime_tower) {
|
||||
for (size_t i = 0; i<lt.extruders.size(); ++i)
|
||||
if (m_print_config_ptr->filament_soluble.get_at(lt.extruders[i]-1)) { // 1-based...
|
||||
if (print_config->filament_soluble.get_at(lt.extruders[i]-1)) { // 1-based...
|
||||
std::swap(lt.extruders[i], lt.extruders.front());
|
||||
break;
|
||||
}
|
||||
@@ -396,6 +427,7 @@ void ToolOrdering::sort_and_build_data(const PrintObject& object , unsigned int
|
||||
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
|
||||
{
|
||||
m_print_full_config = &object.print()->full_print_config();
|
||||
m_print_config_ptr = &object.print()->config();
|
||||
m_print_object_ptr = &object;
|
||||
m_print = const_cast<Print*>(object.print());
|
||||
if (object.layers().empty())
|
||||
@@ -1329,8 +1361,11 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
||||
if (!m_layer_tools.empty())
|
||||
first_layer_filaments = m_layer_tools[0].extruders;
|
||||
|
||||
const bool use_cyclic_ordering =
|
||||
(print_config->toolchange_ordering == ToolChangeOrderingType::Cyclic);
|
||||
|
||||
// other_layers_seq: the layer_idx and extruder_idx are base on 1
|
||||
auto get_custom_seq = [&other_layers_seqs, &reorder_first_layer, &first_layer_filaments](int layer_idx, std::vector<int>& out_seq) -> bool {
|
||||
auto get_custom_seq = [&other_layers_seqs, &reorder_first_layer, &first_layer_filaments, &layer_filaments, use_cyclic_ordering](int layer_idx, std::vector<int>& out_seq) -> bool {
|
||||
if (!reorder_first_layer && layer_idx == 0) {
|
||||
out_seq.resize(first_layer_filaments.size());
|
||||
std::transform(first_layer_filaments.begin(), first_layer_filaments.end(), out_seq.begin(), [](auto item) {return item + 1; });
|
||||
@@ -1343,6 +1378,15 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_cyclic_ordering && layer_idx >= 0 && size_t(layer_idx) < layer_filaments.size()) {
|
||||
std::vector<unsigned int> ordered = layer_filaments[size_t(layer_idx)];
|
||||
std::sort(ordered.begin(), ordered.end());
|
||||
out_seq.resize(ordered.size());
|
||||
std::transform(ordered.begin(), ordered.end(), out_seq.begin(), [](auto item) { return int(item) + 1; });
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
|
||||
m_filament_extruders.clear();
|
||||
//ORCA: Reset current extruder ID and clear pointers to prevent dangling pointers when extruders are recreated.
|
||||
m_curr_extruder_id = -1;
|
||||
m_cached_extruder_idx = 0;
|
||||
std::fill(m_curr_filament_extruder.begin(), m_curr_filament_extruder.end(), nullptr);
|
||||
m_filament_extruders.reserve(extruder_ids.size());
|
||||
for (unsigned int extruder_id : extruder_ids)
|
||||
@@ -617,6 +618,7 @@ std::string GCodeWriter::toolchange(unsigned int filament_id)
|
||||
assert(filament_extruder_iter != m_filament_extruders.end() && filament_extruder_iter->id() == filament_id);
|
||||
m_curr_extruder_id = filament_extruder_iter->extruder_id();
|
||||
m_curr_filament_extruder[m_curr_extruder_id] = &*filament_extruder_iter;
|
||||
m_cached_extruder_idx = get_extruder_index(this->config, filament_id);
|
||||
|
||||
// return the toolchange command
|
||||
// if we are running a single-extruder setup, just set the extruder and return nothing
|
||||
@@ -658,7 +660,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xy(point_on_plate);
|
||||
auto speed = m_is_first_layer
|
||||
? this->config.get_abs_value_at("initial_layer_travel_speed", get_extruder_index(this->config, filament()->id())) : this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx) : this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
w.emit_f(speed * 60.0);
|
||||
//BBS
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
@@ -744,7 +746,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
|
||||
// BBS
|
||||
Vec3d dest_point = point;
|
||||
auto travel_speed =
|
||||
m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", get_extruder_index(this->config, filament()->id())) : this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx) : this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
//BBS: a z_hop need to be handle when travel
|
||||
if (std::abs(m_to_lift) > EPSILON) {
|
||||
assert(std::abs(m_lifted) < EPSILON);
|
||||
@@ -841,13 +843,13 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
|
||||
{
|
||||
//force to move xy first then z after filament change
|
||||
w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y()));
|
||||
w.emit_f(this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id())) * 60.0);
|
||||
w.emit_f(this->config.travel_speed.get_at(m_cached_extruder_idx) * 60.0);
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
out_string = w.string() + _travel_to_z(point_on_plate.z(), comment);
|
||||
} else {
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(point_on_plate);
|
||||
w.emit_f(this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id())) * 60.0);
|
||||
w.emit_f(this->config.travel_speed.get_at(m_cached_extruder_idx) * 60.0);
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
out_string = w.string();
|
||||
}
|
||||
@@ -880,10 +882,10 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
|
||||
{
|
||||
m_pos(2) = z;
|
||||
|
||||
double speed = this->config.travel_speed_z.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
double speed = this->config.travel_speed_z.get_at(m_cached_extruder_idx);
|
||||
if (speed == 0.) {
|
||||
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", get_extruder_index(this->config, filament()->id()))
|
||||
: this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
|
||||
: this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
}
|
||||
|
||||
GCodeG1Formatter w;
|
||||
@@ -897,11 +899,11 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
|
||||
std::string GCodeWriter::_spiral_travel_to_z(double z, const Vec2d &ij_offset, const std::string &comment)
|
||||
{
|
||||
std::string output;
|
||||
double speed = this->config.travel_speed_z.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
double speed = this->config.travel_speed_z.get_at(m_cached_extruder_idx);
|
||||
|
||||
if (speed == 0.) {
|
||||
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", get_extruder_index(this->config, filament()->id()))
|
||||
: this->config.travel_speed.get_at(get_extruder_index(this->config, filament()->id()));
|
||||
speed = m_is_first_layer ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
|
||||
: this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
}
|
||||
|
||||
if (!this->config.enable_arc_fitting) { // Orca: if arc fitting is disabled, approximate the arc with small linear segments
|
||||
@@ -1261,6 +1263,7 @@ void GCodeWriter::init_extruder(unsigned int filament_id)
|
||||
assert(filament_extruder_iter != m_filament_extruders.end() && filament_extruder_iter->id() == filament_id);
|
||||
m_curr_extruder_id = filament_extruder_iter->extruder_id();
|
||||
m_curr_filament_extruder[m_curr_extruder_id] = &*filament_extruder_iter;
|
||||
m_cached_extruder_idx = get_extruder_index(this->config, filament_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ public:
|
||||
GCodeWriter() :
|
||||
multiple_extruders(false), m_curr_filament_extruder(MAXIMUM_EXTRUDER_NUMBER, nullptr),
|
||||
m_curr_extruder_id (-1),
|
||||
m_cached_extruder_idx(0),
|
||||
m_single_extruder_multi_material(false),
|
||||
m_last_acceleration(0), m_max_acceleration(0),m_last_travel_acceleration(0), m_max_travel_acceleration(0),
|
||||
m_last_jerk(0), m_max_jerk_x(0), m_max_jerk_y(0),
|
||||
@@ -135,6 +136,8 @@ public:
|
||||
bool m_single_extruder_multi_material;
|
||||
std::vector<Extruder*> m_curr_filament_extruder;
|
||||
int m_curr_extruder_id;
|
||||
// Motion uses the global/base process variant until a filament becomes active.
|
||||
size_t m_cached_extruder_idx;
|
||||
unsigned int m_last_acceleration;
|
||||
unsigned int m_last_travel_acceleration;
|
||||
std::vector<unsigned int> m_max_travel_acceleration;
|
||||
|
||||
@@ -157,6 +157,10 @@ public:
|
||||
ExPolygons lslices;
|
||||
ExPolygons lslices_extrudable; // BBS: the extrudable part of lslices used for tree support
|
||||
std::vector<BoundingBox> lslices_bboxes;
|
||||
// Orca: for separated infills / per-model centering. Aligned with lslices: for each island, the
|
||||
// full bounding box of the 3D connected body (across all layers) it belongs to. Populated by
|
||||
// PrintObject::infill() only when the feature is used; empty otherwise.
|
||||
std::vector<BoundingBox> lslices_separated_component_bboxes;
|
||||
|
||||
// BBS
|
||||
ExPolygons loverhangs;
|
||||
|
||||
+17
-10
@@ -920,6 +920,13 @@ public:
|
||||
// Extruder ID is only valid for FFF. Returns -1 for SLA or if the extruder ID is not applicable (support volumes).
|
||||
int extruder_id() const;
|
||||
|
||||
//Orca: cache clearing procedure to ensure that the shape is positioned accurately when manipulating it
|
||||
void clear_cache() {
|
||||
m_cached_trans_matrix = Transform3d::Identity().inverse(); // get unvelivable matrix
|
||||
m_convex_hull_2d.clear();
|
||||
m_cached_2d_polygon.clear();
|
||||
};
|
||||
|
||||
bool is_splittable() const;
|
||||
|
||||
// BBS
|
||||
@@ -966,34 +973,34 @@ public:
|
||||
static std::string type_to_string(const ModelVolumeType t);
|
||||
|
||||
const Geometry::Transformation& get_transformation() const { return m_transformation; }
|
||||
void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; }
|
||||
void set_transformation(const Transform3d& trafo) { m_transformation.set_matrix(trafo); }
|
||||
void set_transformation(const Geometry::Transformation& transformation) { clear_cache(); m_transformation = transformation; }
|
||||
void set_transformation(const Transform3d& trafo) { clear_cache(); m_transformation.set_matrix(trafo); }
|
||||
|
||||
Vec3d get_offset() const { return m_transformation.get_offset(); }
|
||||
|
||||
double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
|
||||
|
||||
void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); }
|
||||
void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); }
|
||||
void set_offset(const Vec3d& offset) { clear_cache(); m_transformation.set_offset(offset); }
|
||||
void set_offset(Axis axis, double offset) { clear_cache(); m_transformation.set_offset(axis, offset); }
|
||||
|
||||
Vec3d get_rotation() const { return m_transformation.get_rotation(); }
|
||||
double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); }
|
||||
|
||||
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
|
||||
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
|
||||
void set_rotation(const Vec3d& rotation) { clear_cache(); m_transformation.set_rotation(rotation); }
|
||||
void set_rotation(Axis axis, double rotation) { clear_cache(); m_transformation.set_rotation(axis, rotation); }
|
||||
|
||||
Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
||||
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
|
||||
void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); }
|
||||
void set_scaling_factor(const Vec3d& scaling_factor) { clear_cache(); m_transformation.set_scaling_factor(scaling_factor); }
|
||||
void set_scaling_factor(Axis axis, double scaling_factor) {clear_cache(); m_transformation.set_scaling_factor(axis, scaling_factor); }
|
||||
|
||||
Vec3d get_mirror() const { return m_transformation.get_mirror(); }
|
||||
double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
|
||||
bool is_left_handed() const { return m_transformation.is_left_handed(); }
|
||||
|
||||
void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); }
|
||||
void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); }
|
||||
void set_mirror(const Vec3d& mirror) { clear_cache(); m_transformation.set_mirror(mirror); }
|
||||
void set_mirror(Axis axis, double mirror) { clear_cache(); m_transformation.set_mirror(axis, mirror); }
|
||||
void convert_from_imperial_units();
|
||||
void convert_from_meters();
|
||||
|
||||
|
||||
@@ -571,6 +571,19 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
|
||||
return extrusion_coll;
|
||||
}
|
||||
|
||||
// ORCA: only_one_wall_top detects the top as "slice − upper", so a feature rising from the middle of a
|
||||
// top surface becomes an enclosed hole that gets ringed with extra inner walls. Fill those holes back
|
||||
// into the top. Only holes that are both covered by the upper layer (excludes bridges) and backed by
|
||||
// solid material (excludes voids) are filled.
|
||||
static ExPolygons fill_enclosed_top_feature_holes(const ExPolygons &top, const Polygons &covered_by_upper, const ExPolygons &solid)
|
||||
{
|
||||
ExPolygons filled = top;
|
||||
for (ExPolygon &ex : filled)
|
||||
ex.holes.clear();
|
||||
const ExPolygons feature_holes = intersection_ex(intersection_ex(diff_ex(filled, top), covered_by_upper), solid);
|
||||
return feature_holes.empty() ? top : union_ex(top, feature_holes);
|
||||
}
|
||||
|
||||
void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExPolygons &top_fills,
|
||||
ExPolygons &non_top_polygons, ExPolygons &fill_clip) const {
|
||||
// other perimeters
|
||||
@@ -636,6 +649,8 @@ void PerimeterGenerator::split_top_surfaces(const ExPolygons &orig_polygons, ExP
|
||||
ExPolygons delete_bridge = diff_ex(orig_polygons, bridge_checker, ApplySafetyOffset::Yes);
|
||||
|
||||
ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes);
|
||||
top_polygons = fill_enclosed_top_feature_holes(top_polygons, upper_polygons_series_clipped, orig_polygons);
|
||||
|
||||
// get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the
|
||||
// min_width_top_surface we removed a bit before)
|
||||
ExPolygons temp_gap = diff_ex(top_polygons, fill_clip);
|
||||
@@ -2194,6 +2209,7 @@ void PerimeterGenerator::process_arachne()
|
||||
upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox);
|
||||
|
||||
top_expolygons = diff_ex(infill_contour, upper_slices_clipped);
|
||||
top_expolygons = fill_enclosed_top_feature_holes(top_expolygons, upper_slices_clipped, infill_contour);
|
||||
|
||||
if (!top_expolygons.empty()) {
|
||||
if (lower_slices != nullptr) {
|
||||
|
||||
@@ -712,6 +712,26 @@ namespace client
|
||||
static void regex_matches (expr &lhs, IteratorRange &rhs) { return regex_op(lhs, rhs, '=', lhs); }
|
||||
static void regex_doesnt_match(expr &lhs, IteratorRange &rhs) { return regex_op(lhs, rhs, '!', lhs); }
|
||||
|
||||
// Replace every match of the regular expression 'pattern' in the string 'subject' with 'replacement'.
|
||||
// The replacement may reference capture groups ($1, $2, ...). Store the result into subject.
|
||||
static void regex_replace(expr &subject, IteratorRange &pattern, expr &replacement)
|
||||
{
|
||||
if (subject.type() == TYPE_EMPTY)
|
||||
// Inside an if / else block to be skipped
|
||||
return;
|
||||
if (subject.type() != TYPE_STRING)
|
||||
subject.throw_exception("regex_replace() first parameter must be a string.");
|
||||
try {
|
||||
std::string re(++ pattern.begin(), -- pattern.end());
|
||||
std::string result = SLIC3R_REGEX_NAMESPACE::regex_replace(subject.s(), SLIC3R_REGEX_NAMESPACE::regex(re), replacement.to_string());
|
||||
subject.set_s(std::move(result));
|
||||
} catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) {
|
||||
// Syntax error in the regular expression
|
||||
boost::throw_exception(qi::expectation_failure<Iterator>(
|
||||
pattern.begin(), pattern.end(), spirit::info(std::string("*Regular expression compilation failed: ") + ex.what())));
|
||||
}
|
||||
}
|
||||
|
||||
static void one_of_test_init(expr &out) {
|
||||
out.set_b(false);
|
||||
}
|
||||
@@ -2323,6 +2343,8 @@ namespace client
|
||||
[ px::bind(&expr::digits<false>, _val, _2, _3) ]
|
||||
| (kw["zdigits"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > optional_parameter(_r1))
|
||||
[ px::bind(&expr::digits<true>, _val, _2, _3) ]
|
||||
| (kw["regex_replace"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > regular_expression > ',' > conditional_expression(_r1) > ')')
|
||||
[ px::bind(&expr::regex_replace, _val, _2, _3) ]
|
||||
| (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ]
|
||||
| (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ]
|
||||
| (kw["ceil"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::ceil, _1, _val) ]
|
||||
@@ -2404,6 +2426,7 @@ namespace client
|
||||
("min")
|
||||
("max")
|
||||
("random")
|
||||
("regex_replace")
|
||||
("filament_change")
|
||||
("repeat")
|
||||
("round")
|
||||
|
||||
+15
-10
@@ -892,12 +892,11 @@ std::string Preset::get_printer_type(PresetBundle *preset_bundle)
|
||||
{
|
||||
if (preset_bundle) {
|
||||
auto config = &preset_bundle->printers.get_edited_preset().config;
|
||||
std::string vendor_name;
|
||||
for (auto vendor_profile : preset_bundle->vendors) {
|
||||
for (auto vendor_model : vendor_profile.second.models)
|
||||
if (vendor_model.name == config->opt_string("printer_model"))
|
||||
const auto& printer_model = config->opt_string("printer_model");
|
||||
for (const auto& vendor_profile : preset_bundle->vendors) {
|
||||
for (const auto& vendor_model : vendor_profile.second.models)
|
||||
if (vendor_model.name == printer_model)
|
||||
{
|
||||
vendor_name = vendor_profile.first;
|
||||
return vendor_model.model_id;
|
||||
}
|
||||
}
|
||||
@@ -909,11 +908,10 @@ std::string Preset::get_current_printer_type(PresetBundle *preset_bundle)
|
||||
{
|
||||
if (preset_bundle) {
|
||||
auto config = &(this->config);
|
||||
std::string vendor_name;
|
||||
for (auto vendor_profile : preset_bundle->vendors) {
|
||||
for (auto vendor_model : vendor_profile.second.models)
|
||||
if (vendor_model.name == config->opt_string("printer_model")) {
|
||||
vendor_name = vendor_profile.first;
|
||||
const auto& printer_model = config->opt_string("printer_model");
|
||||
for (const auto& vendor_profile : preset_bundle->vendors) {
|
||||
for (const auto& vendor_model : vendor_profile.second.models)
|
||||
if (vendor_model.name == printer_model) {
|
||||
return vendor_model.model_id;
|
||||
}
|
||||
}
|
||||
@@ -1046,6 +1044,9 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"lightning_prune_angle",
|
||||
"lightning_straightening_angle",
|
||||
"top_surface_pattern",
|
||||
"top_surface_expansion",
|
||||
"top_surface_expansion_margin",
|
||||
"top_surface_expansion_direction",
|
||||
"bottom_surface_pattern",
|
||||
"infill_direction",
|
||||
"solid_infill_direction",
|
||||
@@ -1062,6 +1063,9 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"skin_infill_density",
|
||||
"align_infill_direction_to_model",
|
||||
"extra_solid_infills",
|
||||
"anisotropic_surfaces",
|
||||
"center_of_surface_pattern",
|
||||
"separated_infills",
|
||||
"minimum_sparse_infill_area",
|
||||
"reduce_infill_retraction",
|
||||
"internal_solid_infill_pattern",
|
||||
@@ -1274,6 +1278,7 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"wipe_tower_bridging",
|
||||
"wipe_tower_extra_flow",
|
||||
"single_extruder_multi_material_priming",
|
||||
"toolchange_ordering",
|
||||
"wipe_tower_rotation_angle",
|
||||
"tree_support_branch_distance_organic",
|
||||
"tree_support_branch_diameter_organic",
|
||||
|
||||
@@ -209,6 +209,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"chamber_minimal_temperature",
|
||||
"thumbnails",
|
||||
"thumbnails_format",
|
||||
"anisotropic_surfaces",
|
||||
"center_of_surface_pattern",
|
||||
"separated_infills",
|
||||
"seam_gap",
|
||||
"role_based_wipe_speed",
|
||||
"wipe_speed",
|
||||
@@ -330,6 +333,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "first_layer_print_sequence"
|
||||
|| opt_key == "other_layers_print_sequence"
|
||||
|| opt_key == "other_layers_print_sequence_nums"
|
||||
|| opt_key == "toolchange_ordering"
|
||||
|| opt_key == "extruder_ams_count"
|
||||
|| opt_key == "filament_map_mode"
|
||||
|| opt_key == "filament_map"
|
||||
|
||||
@@ -418,7 +418,7 @@ public:
|
||||
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
||||
const SlicingParameters& slicing_parameters() const { return m_slicing_params; }
|
||||
// Orca: XYZ shrinkage compensation has introduced the const Vec3d &object_shrinkage_compensation parameter to the function below
|
||||
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation);
|
||||
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation, std::vector<int> variant_index = std::vector<int>());
|
||||
|
||||
size_t num_printing_regions() const throw() { return m_shared_regions->all_regions.size(); }
|
||||
const PrintRegion& printing_region(size_t idx) const throw() { return *m_shared_regions->all_regions[idx].get(); }
|
||||
@@ -489,7 +489,7 @@ public:
|
||||
// If ! m_slicing_params.valid, recalculate.
|
||||
void update_slicing_parameters();
|
||||
|
||||
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
|
||||
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders, std::vector<int>& variant_index);
|
||||
|
||||
private:
|
||||
void make_perimeters();
|
||||
|
||||
@@ -724,7 +724,7 @@ PrintObjectRegions::BoundingBox find_modifier_volume_extents(const PrintObjectRe
|
||||
return out;
|
||||
}
|
||||
|
||||
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
|
||||
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders, std::vector<int>& variant_index);
|
||||
|
||||
void print_region_ref_inc(PrintRegion &r) { ++ r.m_ref_cnt; }
|
||||
void print_region_ref_reset(PrintRegion &r) { r.m_ref_cnt = 0; }
|
||||
@@ -738,7 +738,8 @@ bool verify_update_print_object_regions(
|
||||
const PrintRegionConfig &default_region_config,
|
||||
size_t num_extruders,
|
||||
PrintObjectRegions &print_object_regions,
|
||||
const std::function<void(const PrintRegionConfig&, const PrintRegionConfig&, const t_config_option_keys&)> &callback_invalidate)
|
||||
const std::function<void(const PrintRegionConfig&, const PrintRegionConfig&, const t_config_option_keys&)> &callback_invalidate,
|
||||
std::vector<int>& variant_index)
|
||||
{
|
||||
// Sort by ModelVolume ID.
|
||||
model_volumes_sort_by_id(model_volumes);
|
||||
@@ -783,7 +784,7 @@ bool verify_update_print_object_regions(
|
||||
} else if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox))
|
||||
// Such parent region does not exist. If it is needed, then we need to reslice.
|
||||
// Only create new region for a modifier, which actually modifies config of it's parent.
|
||||
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders);
|
||||
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders, variant_index);
|
||||
config != parent_region.region->config())
|
||||
// This modifier newly overrides a region, which it did not before. We need to reslice.
|
||||
return false;
|
||||
@@ -791,8 +792,8 @@ bool verify_update_print_object_regions(
|
||||
}
|
||||
}
|
||||
PrintRegionConfig cfg = region.parent == -1 ?
|
||||
region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders) :
|
||||
region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders);
|
||||
region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders, variant_index) :
|
||||
region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders, variant_index);
|
||||
if (cfg != region.region->config()) {
|
||||
// Region configuration changed.
|
||||
if (print_region_ref_cnt(*region.region) == 0) {
|
||||
@@ -964,6 +965,7 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
size_t num_extruders,
|
||||
const float xy_contour_compensation,
|
||||
const std::vector<unsigned int> &painting_extruders,
|
||||
std::vector<int> &variant_index,
|
||||
const bool has_painted_fuzzy_skin)
|
||||
{
|
||||
// Reuse the old object or generate a new one.
|
||||
@@ -1022,7 +1024,7 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
// Add a model volume, assign an existing region or generate a new one.
|
||||
layer_range.volume_regions.push_back({
|
||||
&volume, -1,
|
||||
get_create_region(region_config_from_model_volume(default_region_config, layer_range.config, volume, num_extruders)),
|
||||
get_create_region(region_config_from_model_volume(default_region_config, layer_range.config, volume, num_extruders, variant_index)),
|
||||
bbox
|
||||
});
|
||||
} else if (volume.is_negative_volume()) {
|
||||
@@ -1039,7 +1041,7 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
if (parent_volume.is_model_part() || parent_volume.is_modifier())
|
||||
if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox)) {
|
||||
// Only create new region for a modifier, which actually modifies config of it's parent.
|
||||
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders);
|
||||
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders, variant_index);
|
||||
config != parent_region.region->config()) {
|
||||
added = true;
|
||||
layer_range.volume_regions.push_back({ &volume, parent_region_id, get_create_region(std::move(config)), bbox });
|
||||
@@ -1162,12 +1164,13 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
}
|
||||
|
||||
//apply extruder related values
|
||||
std::vector<int> print_variant_index;
|
||||
if (!extruder_applied) {
|
||||
// variant_2 must be processed first, because variant_1 will make `printer_extruder_id` and `printer_extruder_variant` half of the size that makes `get_index_for_extruder` no longer work properly
|
||||
new_full_config.update_values_to_printer_extruders(new_full_config, printer_options_with_variant_2, "printer_extruder_id", "printer_extruder_variant", 2);
|
||||
new_full_config.update_values_to_printer_extruders(new_full_config, printer_options_with_variant_1, "printer_extruder_id", "printer_extruder_variant");
|
||||
//update print config related with variants
|
||||
new_full_config.update_values_to_printer_extruders(new_full_config, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
||||
print_variant_index = new_full_config.update_values_to_printer_extruders(new_full_config, print_options_with_variant, "print_extruder_id", "print_extruder_variant");
|
||||
|
||||
m_ori_full_print_config = new_full_config;
|
||||
new_full_config.update_values_to_printer_extruders_for_multiple_filaments(new_full_config, filament_options_with_variant, "filament_self_index", "filament_extruder_variant");
|
||||
@@ -1475,7 +1478,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
if (object_config_changed)
|
||||
model_object.config.assign_config(model_object_new.config);
|
||||
if (! object_diff.empty() || object_config_changed || num_extruders_changed ) {
|
||||
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders );
|
||||
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders, print_variant_index);
|
||||
for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(model_object)) {
|
||||
t_config_option_keys diff = print_object_status.print_object->config().diff(new_config);
|
||||
if (! diff.empty()) {
|
||||
@@ -1541,10 +1544,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
||||
// Producing the config for PrintObject on demand, caching it at print_object_last.
|
||||
const PrintObject *print_object_last = nullptr;
|
||||
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders ](PrintObject *print_object) {
|
||||
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders, &print_variant_index](PrintObject *print_object) {
|
||||
print_object->config_apply(print_object_last ?
|
||||
print_object_last->config() :
|
||||
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders ));
|
||||
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders, print_variant_index));
|
||||
print_object_last = print_object;
|
||||
};
|
||||
if (old.empty()) {
|
||||
@@ -1715,7 +1718,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
||||
if ((*it)->m_shared_regions != nullptr)
|
||||
update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
|
||||
})) {
|
||||
},
|
||||
print_variant_index)) {
|
||||
// Regions are valid, just keep them.
|
||||
} else {
|
||||
// Regions were reshuffled.
|
||||
@@ -1737,6 +1741,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
num_extruders ,
|
||||
print_object.is_mm_painted() ? 0.f : float(print_object.config().xy_contour_compensation.value),
|
||||
painting_extruders,
|
||||
print_variant_index,
|
||||
print_object.is_fuzzy_skin_painted());
|
||||
}
|
||||
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
||||
|
||||
+155
-11
@@ -188,6 +188,12 @@ static t_config_enum_values s_keys_map_PowerLossRecoveryMode {
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PowerLossRecoveryMode)
|
||||
|
||||
static t_config_enum_values s_keys_map_CenterOfSurfacePattern{
|
||||
{"each_surface", int(CenterOfSurfacePattern::Each_Surface)},
|
||||
{"each_model", int(CenterOfSurfacePattern::Each_Model)},
|
||||
{"each_assembly", int(CenterOfSurfacePattern::Each_Assembly)}};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(CenterOfSurfacePattern)
|
||||
|
||||
static t_config_enum_values s_keys_map_FuzzySkinType {
|
||||
{ "none", int(FuzzySkinType::None) },
|
||||
{ "external", int(FuzzySkinType::External) },
|
||||
@@ -221,6 +227,13 @@ static t_config_enum_values s_keys_map_FuzzySkinMode {
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(FuzzySkinMode)
|
||||
|
||||
static t_config_enum_values s_keys_map_TopSurfaceExpansionDirection {
|
||||
{ "inward_and_outward", int(TopSurfaceExpansionDirection::InwardAndOutward) },
|
||||
{ "inward", int(TopSurfaceExpansionDirection::Inward) },
|
||||
{ "outward", int(TopSurfaceExpansionDirection::Outward) }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TopSurfaceExpansionDirection)
|
||||
|
||||
static t_config_enum_values s_keys_map_InfillPattern {
|
||||
{ "monotonic", ipMonotonic },
|
||||
{ "monotonicline", ipMonotonicLine },
|
||||
@@ -522,6 +535,12 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType{
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType)
|
||||
|
||||
static t_config_enum_values s_keys_map_ToolChangeOrderingType {
|
||||
{ "default", int(ToolChangeOrderingType::Default) },
|
||||
{ "cyclic", int(ToolChangeOrderingType::Cyclic) }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ToolChangeOrderingType)
|
||||
|
||||
static const t_config_enum_values s_keys_map_ZHopType = {
|
||||
{ "Auto Lift", zhtAuto },
|
||||
{ "Normal Lift", zhtNormal },
|
||||
@@ -1236,7 +1255,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If left to zero, the bridging angle will be calculated automatically for each specific bridge.\n"
|
||||
"Otherwise the provided angle will be used according to:\n"
|
||||
" - The absolute coordinates\n"
|
||||
" - The absolute coordinates + Model rotation: If Align infill direction to model is enabled\n"
|
||||
" - The absolute coordinates + Model rotation: If Align directions to model is enabled\n"
|
||||
" - The optimal automatic angle + this value: If 'Relative Bridge Angle' is enabled\n\n"
|
||||
"Use 180° for zero absolute angle.");
|
||||
def->sidetext = u8"°"; // degrees, don't need translation
|
||||
@@ -1253,7 +1272,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"If left to zero, the bridging angle will be calculated automatically for each specific bridge.\n"
|
||||
"Otherwise the provided angle will be used according to:\n"
|
||||
" - The absolute coordinates\n"
|
||||
" - The absolute coordinates + Model rotation: If Align infill direction to model is enabled\n"
|
||||
" - The absolute coordinates + Model rotation: If Align directions to model is enabled\n"
|
||||
" - The optimal automatic angle + this value: If 'Relative Bridge Angle' is enabled\n\n"
|
||||
"Use 180° for zero absolute angle.");
|
||||
def->sidetext = u8"°"; // degrees, don't need translation
|
||||
@@ -2117,6 +2136,47 @@ void PrintConfigDef::init_fff_params()
|
||||
def->max = 100;
|
||||
def->set_default_value(new ConfigOptionPercent(100));
|
||||
|
||||
def = this->add("top_surface_expansion", coFloat);
|
||||
def->label = L("Top surface expansion");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Expands the top surfaces by this distance to connect distinct top surfaces and fill gaps.\n"
|
||||
"Useful for cases where the top surface is interrupted by a raised feature, such as text on a plane."
|
||||
"Expanding it removes the holes beneath these features and creates a continuous path with a better finish for printing on top."
|
||||
"The expansion is applied to the original top surface, before any other processing such as bridging or overhang detection.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("top_surface_expansion_margin", coFloat);
|
||||
def->label = L("Top expansion wall margin");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Using “Top surface expansion” may cause a surface that did not previously touch the model's outer walls to now do so.\n"
|
||||
"This can cause contraction marks (such as the hull line) on the outer walls.\n"
|
||||
"By adding a small margin, this contraction will not occur directly on the walls, thereby preventing a visible mark.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->max = 10;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("top_surface_expansion_direction", coEnum);
|
||||
def->label = L("Top expansion direction");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Direction in which the top surface expansion grows.\n"
|
||||
" - Inward grows into the holes and gaps left by features rising from the middle of a top surface.\n"
|
||||
" - Outward grows the outer edge of the surface, connecting surfaces separated by features that can divide a surface, such as a lattice pattern.\n"
|
||||
" - Inward and Outward does both.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<TopSurfaceExpansionDirection>::get_enum_values();
|
||||
def->enum_values.push_back("inward_and_outward");
|
||||
def->enum_values.push_back("inward");
|
||||
def->enum_values.push_back("outward");
|
||||
def->enum_labels.push_back(L("Inward and Outward"));
|
||||
def->enum_labels.push_back(L("Inward"));
|
||||
def->enum_labels.push_back(L("Outward"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<TopSurfaceExpansionDirection>(TopSurfaceExpansionDirection::InwardAndOutward));
|
||||
|
||||
def = this->add("bottom_surface_pattern", coEnum);
|
||||
def->label = L("Bottom surface pattern");
|
||||
def->category = L("Strength");
|
||||
@@ -3035,12 +3095,13 @@ void PrintConfigDef::init_fff_params()
|
||||
def->min = 0;
|
||||
def->max = 100;
|
||||
def->set_default_value(new ConfigOptionPercent(20));
|
||||
|
||||
|
||||
def = this->add("align_infill_direction_to_model", coBool);
|
||||
def->label = L("Align infill direction to model");
|
||||
def->label = L("Align directions to model");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Aligns infill, bridge, ironing and surface fill directions to follow the model's orientation on the build plate.\n"
|
||||
"When enabled, directions rotate with the model to maintain optimal strength characteristics.");
|
||||
def->tooltip = L("Aligns infill, bridge, ironing, and top/bottom surface directions to follow the model's orientation on the build plate.\n"
|
||||
"When enabled, these directions rotate together with the model so the printed features keep their intended orientation "
|
||||
"relative to the part, preserving optimal strength and surface characteristics regardless of how the model is placed.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
@@ -6076,6 +6137,22 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("toolchange_ordering", coEnum);
|
||||
def->label = L("Toolchange ordering");
|
||||
def->category = L("Advanced");
|
||||
def->tooltip = L(
|
||||
"Determines the order of tool changes on each layer.\n"
|
||||
"- Default: Starts with the last used extruder to minimize tool changes.\n"
|
||||
"- Cyclic: Uses a fixed tool sequence each layer. This sacrifices speed for better surface quality, as the extra toolchanges allow layers more time to cool."
|
||||
);
|
||||
def->mode = comAdvanced;
|
||||
def->enum_keys_map = &ConfigOptionEnum<ToolChangeOrderingType>::get_enum_values();
|
||||
def->enum_values.emplace_back("default");
|
||||
def->enum_values.emplace_back("cyclic");
|
||||
def->enum_labels.emplace_back(L("Default"));
|
||||
def->enum_labels.emplace_back(L("Cyclic"));
|
||||
def->set_default_value(new ConfigOptionEnum<ToolChangeOrderingType>(ToolChangeOrderingType::Default));
|
||||
|
||||
def = this->add("slice_closing_radius", coFloat);
|
||||
def->label = L("Slice gap closing radius");
|
||||
def->category = L("Quality");
|
||||
@@ -6799,6 +6876,47 @@ void PrintConfigDef::init_fff_params()
|
||||
def->min = 0;
|
||||
def->set_default_value(new ConfigOptionFloat(0.6));
|
||||
|
||||
def = this->add("anisotropic_surfaces", coBool);
|
||||
def->label = L("Anisotropic surfaces");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Anisotropic patterns on the top and bottom surfaces.\n"
|
||||
"Co-directional printing mode will be applied. For certain patterns, omni-directional filling provides color "
|
||||
"dispersion when using multi-colored or silk plastics.\n"
|
||||
"This option disable the gap fill.\n"
|
||||
"This option can increase a printing time.");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("separated_infills", coBool);
|
||||
def->label = L("Separated infills");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Centers the internal infill of each part on itself, as if it were sliced on its own, instead of on the "
|
||||
"whole assembly. Parts that touch or overlap are treated as one body and share a center; separate parts "
|
||||
"(or distinct 3D objects) each get their own.\n"
|
||||
"Useful when an assembly groups several objects that should each keep a consistent, self-centered infill.\n"
|
||||
"Affects line and grid patterns and rotation-template infills.\n"
|
||||
"Patterns locked to global coordinates (Gyroid, Honeycomb, TPMS, ...) are unaffected.");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("center_of_surface_pattern", coEnum);
|
||||
def->label = L("Center surface pattern on");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Chooses where the centering point of centered top/bottom surface patterns (Archimedean Chords, "
|
||||
"Octagram Spiral) is placed.\n"
|
||||
" - Each Surface: centers the pattern on every individual surface region, so each island is symmetric on its own.\n"
|
||||
" - Each Model: centers the pattern on each connected body. Parts that touch or overlap share one center; "
|
||||
"parts detached from the rest each get their own.\n"
|
||||
" - Each Assembly: uses a single shared center for the whole object or assembly.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<CenterOfSurfacePattern>::get_enum_values();
|
||||
def->enum_values.push_back("each_surface");
|
||||
def->enum_values.push_back("each_model");
|
||||
def->enum_values.push_back("each_assembly");
|
||||
def->enum_labels.push_back(L("Each Surface"));
|
||||
def->enum_labels.push_back(L("Each Model"));
|
||||
def->enum_labels.push_back(L("Each Assembly"));
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionEnum<CenterOfSurfacePattern>(CenterOfSurfacePattern::Each_Surface));
|
||||
|
||||
def = this->add("travel_speed", coFloats);
|
||||
def->label = L("Travel");
|
||||
@@ -9519,7 +9637,7 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
|
||||
bool has_value = false;
|
||||
double target_value = std::numeric_limits<double>::max();
|
||||
for(auto idx : indices){
|
||||
if(opt && !opt->is_nil(idx)){
|
||||
if(opt && idx < opt->values.size() && !opt->is_nil(idx)){
|
||||
has_value = true;
|
||||
target_value = std::min(target_value, src_values[idx]);
|
||||
}
|
||||
@@ -9706,10 +9824,12 @@ DynamicPrintConfig::get_filament_type() const
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride, unsigned int extruder_id)
|
||||
std::vector<int> DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride, unsigned int extruder_id)
|
||||
{
|
||||
int extruder_count;
|
||||
bool different_extruder = printer_config.support_different_extruders(extruder_count);
|
||||
std::vector<int> variant_index;
|
||||
|
||||
if ((extruder_count > 1) || different_extruder)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", Line %1%: different extruders processing")%__LINE__;
|
||||
@@ -9720,9 +9840,9 @@ void DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig&
|
||||
auto opt_nozzle_volume_type = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_config.option("nozzle_volume_type"));
|
||||
if (!opt_extruder_type || !opt_nozzle_volume_type) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: extruder_type or nozzle_volume_type option not found, skipping")%__LINE__;
|
||||
return;
|
||||
return variant_index;
|
||||
}
|
||||
std::vector<int> variant_index;
|
||||
|
||||
|
||||
if (extruder_id > 0 && extruder_id <= static_cast<unsigned> (extruder_count)) {
|
||||
variant_index.resize(1);
|
||||
@@ -9761,7 +9881,7 @@ void DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig&
|
||||
const ConfigDef *config_def = this->def();
|
||||
if (!config_def) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: can not find config define")%__LINE__;
|
||||
return;
|
||||
return variant_index;
|
||||
}
|
||||
for (auto& key: key_set)
|
||||
{
|
||||
@@ -9875,6 +9995,8 @@ void DynamicPrintConfig::update_values_to_printer_extruders(DynamicPrintConfig&
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return variant_index;
|
||||
}
|
||||
|
||||
void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filaments(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name)
|
||||
@@ -10407,6 +10529,28 @@ void compute_filament_override_value(const std::string& opt_key, const ConfigOpt
|
||||
}
|
||||
|
||||
|
||||
void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride)
|
||||
{
|
||||
if (variant_index.size() > 0) {
|
||||
const t_config_option_keys &keys = dest_config.keys();
|
||||
for (auto& opt : keys) {
|
||||
ConfigOption *opt_src = config.option(opt);
|
||||
const ConfigOption *opt_dest = dest_config.option(opt);
|
||||
if (opt_src && opt_dest && (*opt_src != *opt_dest)) {
|
||||
if (opt_dest->is_scalar() || (key_set1.find(opt) == key_set1.end()))
|
||||
opt_src->set(opt_dest);
|
||||
else {
|
||||
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
|
||||
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(opt_dest);
|
||||
opt_vec_src->set_to_index(opt_vec_dest, variant_index, stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
config.apply(dest_config, true);
|
||||
}
|
||||
|
||||
//BBS: pass map to recording all invalid valies
|
||||
//FIXME localize this function.
|
||||
std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool under_cli)
|
||||
|
||||
@@ -62,6 +62,19 @@ enum class FuzzySkinMode {
|
||||
Combined,
|
||||
};
|
||||
|
||||
// ORCA: direction in which top_surface_expansion grows the top surfaces.
|
||||
enum class TopSurfaceExpansionDirection {
|
||||
InwardAndOutward,
|
||||
Inward,
|
||||
Outward,
|
||||
};
|
||||
|
||||
enum class CenterOfSurfacePattern {
|
||||
Each_Surface,
|
||||
Each_Model,
|
||||
Each_Assembly,
|
||||
};
|
||||
|
||||
enum class NoiseType {
|
||||
Classic,
|
||||
Perlin,
|
||||
@@ -97,6 +110,34 @@ enum InfillPattern : int {
|
||||
ipCount,
|
||||
};
|
||||
|
||||
// Orca: Infill patterns whose alignment origin follows the fill bounding box, so the
|
||||
// "separated_infills" option can re-center them per connected body. Patterns evaluated in
|
||||
// absolute/global coordinates (Gyroid, TPMS, Honeycomb, CrossHatch, ...) or that are shape-relative
|
||||
// (Concentric) ignore that bounding box and are therefore excluded.
|
||||
inline bool is_separable_infill_pattern(InfillPattern pattern)
|
||||
{
|
||||
switch (pattern) {
|
||||
case ipRectilinear:
|
||||
case ipAlignedRectilinear:
|
||||
case ipZigZag:
|
||||
case ipCrossZag:
|
||||
case ipLockedZag:
|
||||
case ipGrid:
|
||||
case ipTriangles:
|
||||
case ipStars: // tri-hexagon
|
||||
case ipCubic:
|
||||
case ipQuarterCubic:
|
||||
case ipLateralHoneycomb:
|
||||
case ipLateralLattice:
|
||||
case ipHilbertCurve:
|
||||
case ipArchimedeanChords:
|
||||
case ipOctagramSpiral:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
enum class IroningType {
|
||||
NoIroning,
|
||||
TopSurfaces,
|
||||
@@ -300,6 +341,12 @@ enum class PerimeterGeneratorType
|
||||
Arachne
|
||||
};
|
||||
|
||||
enum class ToolChangeOrderingType
|
||||
{
|
||||
Default,
|
||||
Cyclic,
|
||||
};
|
||||
|
||||
// BBS
|
||||
enum OverhangFanThreshold {
|
||||
Overhang_threshold_none = 0,
|
||||
@@ -534,6 +581,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrinterTechnology)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(FuzzySkinType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(FuzzySkinMode)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TopSurfaceExpansionDirection)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(WipeTowerType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NoiseType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(InfillPattern)
|
||||
@@ -561,6 +609,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrintHostType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(WipeTowerWallType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ToolChangeOrderingType)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PowerLossRecoveryMode)
|
||||
|
||||
#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS
|
||||
@@ -671,7 +720,7 @@ public:
|
||||
bool is_using_different_extruders();
|
||||
bool support_different_extruders(int& extruder_count) const;
|
||||
int get_index_for_extruder(int extruder_or_filament_id, std::string id_name, ExtruderType extruder_type, NozzleVolumeType nozzle_volume_type, std::string variant_name, unsigned int stride = 1) const;
|
||||
void update_values_to_printer_extruders(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride = 1, unsigned int extruder_id = 0);
|
||||
std::vector<int> update_values_to_printer_extruders(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name, unsigned int stride = 1, unsigned int extruder_id = 0);
|
||||
void update_values_to_printer_extruders_for_multiple_filaments(DynamicPrintConfig& printer_config, std::set<std::string>& key_set, std::string id_name, std::string variant_name);
|
||||
|
||||
void update_non_diff_values_to_base_config(DynamicPrintConfig& new_config, const t_config_option_keys& keys, const std::set<std::string>& different_keys, std::string extruder_id_name, std::string extruder_variant_name,
|
||||
@@ -700,6 +749,7 @@ extern std::set<std::string> empty_options;
|
||||
|
||||
extern std::set<std::string> filament_dev_options;
|
||||
|
||||
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
|
||||
extern void compute_filament_override_value(const std::string& opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig& new_full_config,
|
||||
t_config_option_keys& diff_keys, DynamicPrintConfig& filament_overrides, std::vector<int>& f_maps);
|
||||
|
||||
@@ -1124,6 +1174,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, lightning_prune_angle))
|
||||
((ConfigOptionFloat, lightning_straightening_angle))
|
||||
((ConfigOptionBool, align_infill_direction_to_model))
|
||||
((ConfigOptionBool, anisotropic_surfaces))
|
||||
((ConfigOptionEnum<CenterOfSurfacePattern>, center_of_surface_pattern))
|
||||
((ConfigOptionBool, separated_infills))
|
||||
((ConfigOptionString, extra_solid_infills))
|
||||
((ConfigOptionEnum<FuzzySkinType>, fuzzy_skin))
|
||||
((ConfigOptionFloat, fuzzy_skin_thickness))
|
||||
@@ -1189,6 +1242,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloatOrPercent, top_surface_line_width))
|
||||
((ConfigOptionInt, top_shell_layers))
|
||||
((ConfigOptionFloat, top_shell_thickness))
|
||||
((ConfigOptionFloat, top_surface_expansion))
|
||||
((ConfigOptionFloat, top_surface_expansion_margin))
|
||||
((ConfigOptionEnum<TopSurfaceExpansionDirection>, top_surface_expansion_direction))
|
||||
((ConfigOptionFloatsNullable, top_surface_speed))
|
||||
//BBS
|
||||
((ConfigOptionBoolsNullable, enable_overhang_speed))
|
||||
@@ -1415,6 +1471,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionBool, single_extruder_multi_material))
|
||||
((ConfigOptionBool, manual_filament_change))
|
||||
((ConfigOptionBool, single_extruder_multi_material_priming))
|
||||
((ConfigOptionEnum<ToolChangeOrderingType>, toolchange_ordering))
|
||||
((ConfigOptionBool, wipe_tower_no_sparse_layers))
|
||||
((ConfigOptionString, change_filament_gcode))
|
||||
((ConfigOptionString, change_extrusion_role_gcode))
|
||||
|
||||
+166
-20
@@ -4,6 +4,7 @@
|
||||
#include "Print.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Clipper2Utils.hpp"
|
||||
#include "ElephantFootCompensation.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "I18N.hpp"
|
||||
@@ -97,7 +98,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transfor
|
||||
// snug height and an approximate bounding box in XY.
|
||||
BoundingBoxf3 bbox = model_object->raw_bounding_box();
|
||||
Vec3d bbox_center = bbox.center();
|
||||
|
||||
|
||||
// We may need to rotate the bbox / bbox_center from the original instance to the current instance.
|
||||
double z_diff = Geometry::rotation_diff_z(model_object->instances.front()->get_rotation(), instances.front().model_instance->get_rotation());
|
||||
if (std::abs(z_diff) > EPSILON) {
|
||||
@@ -704,6 +705,72 @@ void PrintObject::infill()
|
||||
|
||||
if (this->set_started(posInfill)) {
|
||||
m_print->set_status(35, L("Generating infill toolpath"));
|
||||
|
||||
// Orca: precompute the object's 3D connected bodies for separated infills / per-model
|
||||
// centering. Two islands belong to the same body when their slices overlap on adjacent
|
||||
// layers; islands that only overlap in top-down projection but never touch (e.g. interleaved
|
||||
// chain links) stay separate, matching "split to objects". Each layer island then records
|
||||
// the full bounding box of its body, so its infill is centered on that body as if it were
|
||||
// sliced alone. Done once here, before the parallel fill, and only when a region needs it.
|
||||
bool needs_separated_components = false;
|
||||
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
|
||||
const PrintRegionConfig &rc = this->printing_region(i).config();
|
||||
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) {
|
||||
needs_separated_components = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fast path: the feature only changes anything when the object is made of more than one
|
||||
// connected body. Detect that cheaply the same way as "Split to objects" — more than one
|
||||
// model part, or a single part whose mesh is splittable (is_splittable() is cached). A single
|
||||
// body already shares the object center, i.e. the default, so skip the connectivity pass.
|
||||
if (needs_separated_components) {
|
||||
int parts = 0;
|
||||
const ModelVolume *first_part = nullptr;
|
||||
for (const ModelVolume *v : this->model_object()->volumes)
|
||||
if (v->is_model_part()) { ++ parts; first_part = v; }
|
||||
if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
|
||||
needs_separated_components = false;
|
||||
}
|
||||
for (Layer *layer : m_layers)
|
||||
layer->lslices_separated_component_bboxes.clear();
|
||||
if (needs_separated_components) {
|
||||
const size_t nl = m_layers.size();
|
||||
std::vector<size_t> offset(nl + 1, 0); // flat index of the first island of each layer
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
offset[i + 1] = offset[i] + m_layers[i]->lslices.size();
|
||||
const size_t nreg = offset[nl];
|
||||
// Union-find over every (layer, island).
|
||||
std::vector<size_t> parent(nreg);
|
||||
for (size_t i = 0; i < nreg; ++ i) parent[i] = i;
|
||||
auto find = [&parent](size_t x) {
|
||||
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
|
||||
return x;
|
||||
};
|
||||
auto unite = [&](size_t a, size_t b) { a = find(a); b = find(b); if (a != b) parent[a] = b; };
|
||||
// Join islands that overlap between two consecutive layers.
|
||||
for (size_t i = 0; i + 1 < nl; ++ i) {
|
||||
const Layer *la = m_layers[i], *lb = m_layers[i + 1];
|
||||
for (size_t a = 0; a < la->lslices.size(); ++ a)
|
||||
for (size_t b = 0; b < lb->lslices.size(); ++ b)
|
||||
if (la->lslices_bboxes[a].overlap(lb->lslices_bboxes[b]) &&
|
||||
! intersection_ex(la->lslices[a], lb->lslices[b]).empty())
|
||||
unite(offset[i] + a, offset[i + 1] + b);
|
||||
}
|
||||
// Full bounding box of each body, indexed by its union-find root.
|
||||
std::vector<BoundingBox> body_bbox(nreg);
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a)
|
||||
body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]);
|
||||
// Store the body bbox for every island.
|
||||
for (size_t i = 0; i < nl; ++ i) {
|
||||
Layer *layer = m_layers[i];
|
||||
layer->lslices_separated_component_bboxes.resize(layer->lslices.size());
|
||||
for (size_t a = 0; a < layer->lslices.size(); ++ a)
|
||||
layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)];
|
||||
}
|
||||
}
|
||||
|
||||
const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first;
|
||||
const auto& support_fill_octree = this->m_adaptive_fill_octrees.second;
|
||||
|
||||
@@ -1297,6 +1364,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "infill_combination_max_layer_height"
|
||||
|| opt_key == "bottom_shell_thickness"
|
||||
|| opt_key == "top_shell_thickness"
|
||||
|| opt_key == "top_surface_expansion"
|
||||
|| opt_key == "top_surface_expansion_margin"
|
||||
|| opt_key == "top_surface_expansion_direction"
|
||||
|| opt_key == "minimum_sparse_infill_area"
|
||||
|| opt_key == "sparse_infill_filament_id"
|
||||
|| opt_key == "internal_solid_filament_id"
|
||||
@@ -1330,6 +1400,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "top_surface_line_width"
|
||||
|| opt_key == "top_surface_density"
|
||||
|| opt_key == "bottom_surface_density"
|
||||
|| opt_key == "anisotropic_surfaces"
|
||||
|| opt_key == "center_of_surface_pattern"
|
||||
|| opt_key == "separated_infills"
|
||||
|| opt_key == "initial_layer_line_width"
|
||||
|| opt_key == "small_area_infill_flow_compensation"
|
||||
|| opt_key == "lateral_lattice_angle_1"
|
||||
@@ -1686,6 +1759,69 @@ void PrintObject::detect_surfaces_type()
|
||||
}
|
||||
}
|
||||
|
||||
// ORCA: Expand the top surfaces outward by top_surface_expansion in every direction. This
|
||||
// enlarges the top solid infill and, in particular, grows it over the covered material left
|
||||
// by features rising from the middle of a top surface (filling holes and joining tops so the
|
||||
// features rest on it). The expansion stays inside the section it belongs to: each connected
|
||||
// solid island has its own outer wall, so the top is grown within each island separately and
|
||||
// clipped to it - growing one island's top across the gap into another island (which may have
|
||||
// no top surface, leaving a partially filled layer) is never allowed. The top infill sits
|
||||
// inside the perimeters, so the margin is measured from the walls: the island is inset by the
|
||||
// band the walls consume (outer wall + inner walls) plus the configured margin, making that
|
||||
// value the real clearance between the expanded top and the walls (avoiding a hull line). The
|
||||
// original top is unioned back in, so where it already sits within that band it is kept as-is.
|
||||
// Never claims a bottom surface.
|
||||
const double top_expansion = layerm->region().config().top_surface_expansion.value;
|
||||
if (top_expansion > 0. && ! top.empty()) {
|
||||
const double d = scale_(top_expansion);
|
||||
const auto jt = Clipper2Lib::JoinType::Miter;
|
||||
const ExPolygons T = union_ex(to_expolygons(top));
|
||||
const int wall_loops = layerm->region().config().wall_loops.value;
|
||||
const double wall_band = wall_loops <= 0 ? 0. :
|
||||
double(layerm->flow(frExternalPerimeter).scaled_width()) +
|
||||
double(layerm->flow(frPerimeter).scaled_width()) * double(wall_loops - 1);
|
||||
const double margin = scale_(layerm->region().config().top_surface_expansion_margin.value);
|
||||
// minimum real top to act on: ignore anything thinner than ~2 top-infill lines
|
||||
const float min_top = float(layerm->flow(frTopSolidInfill).scaled_width());
|
||||
const auto direction = layerm->region().config().top_surface_expansion_direction.value;
|
||||
|
||||
ExPolygons grown;
|
||||
for (const ExPolygon &island : union_ex(layerm_slices_surfaces)) {
|
||||
// The top infill only exists inside the perimeters, so seed and measure from the infill
|
||||
// region (the island minus the wall band), not the raw slice. A section whose only
|
||||
// exposed top lies in the wall band - i.e. a layer where the top is just the walls
|
||||
// themselves - has no infill here and is skipped, instead of being flooded inward by
|
||||
// the expansion. Thin slivers inside the infill region are dropped by the opening too.
|
||||
const ExPolygons infill_region = wall_band > 0. ? offset_ex(island, -float(wall_band)) : ExPolygons{ island };
|
||||
const ExPolygons island_top = intersection_ex(T, infill_region);
|
||||
if (opening_ex(island_top, min_top).empty())
|
||||
continue; // no real top infill in this section - never expand into it
|
||||
|
||||
// grow by d, then keep only the part allowed by the configured direction: inward fills
|
||||
// the holes/gaps left by features (clip the growth back to the top's own filled outline,
|
||||
// which leaves the outer edge fixed), outward grows the outer edge toward the walls (drop
|
||||
// the growth that fell into the original holes), and inward+outward keeps both.
|
||||
ExPolygons expanded = offset_ex_2(island_top, d, jt);
|
||||
if (direction != TopSurfaceExpansionDirection::InwardAndOutward) {
|
||||
ExPolygons outline; // the top with its holes filled (same outer edge)
|
||||
outline.reserve(island_top.size());
|
||||
for (const ExPolygon &ex : island_top)
|
||||
outline.emplace_back(ex.contour);
|
||||
outline = union_ex(outline);
|
||||
expanded = direction == TopSurfaceExpansionDirection::Inward ?
|
||||
intersection_ex(expanded, outline) : // only growth into the holes
|
||||
diff_ex(expanded, diff_ex(outline, island_top)); // only growth past the outer edge
|
||||
}
|
||||
// hold the expansion clear of the walls by the configured margin
|
||||
const ExPolygons allowed = margin > 0. ? offset_ex(infill_region, -float(margin)) : infill_region;
|
||||
append(grown, intersection_ex(expanded, allowed));
|
||||
}
|
||||
|
||||
ExPolygons new_top = diff_ex(union_ex(T, grown), to_expolygons(bottom));
|
||||
top.clear();
|
||||
surfaces_append(top, std::move(new_top), stTop);
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
static int iRun = 0;
|
||||
@@ -2182,7 +2318,7 @@ void PrintObject::discover_vertical_shells()
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
Flow solid_infill_flow = layerm->flow(frSolidInfill);
|
||||
coord_t infill_line_spacing = solid_infill_flow.scaled_spacing();
|
||||
coord_t infill_line_spacing = solid_infill_flow.scaled_spacing();
|
||||
// Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
|
||||
Polygons shell;
|
||||
Polygons holes;
|
||||
@@ -2224,7 +2360,7 @@ void PrintObject::discover_vertical_shells()
|
||||
shell = std::move(shells2);
|
||||
else if (! shells2.empty()) {
|
||||
polygons_append(shell, shells2);
|
||||
// Running the union_ using the Clipper library piece by piece is cheaper
|
||||
// Running the union_ using the Clipper library piece by piece is cheaper
|
||||
// than running the union_ all at once.
|
||||
shell = union_(shell);
|
||||
}
|
||||
@@ -2291,12 +2427,12 @@ void PrintObject::discover_vertical_shells()
|
||||
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell));
|
||||
svg.draw(shell);
|
||||
svg.draw_outline(shell, "black", scale_(0.05));
|
||||
svg.Close();
|
||||
svg.Close();
|
||||
}
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
#if 0
|
||||
// shell = union_(shell, true);
|
||||
shell = union_(shell, false);
|
||||
shell = union_(shell, false);
|
||||
#endif
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
shell_ex = union_safety_offset_ex(shell);
|
||||
@@ -2600,7 +2736,7 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
}
|
||||
|
||||
// LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the
|
||||
// LIGHTNING INFILL SECTION - If lightning infill is used somewhere, we check the areas that are going to be bridges, and those that rely on the
|
||||
// lightning infill under them get expanded. This somewhat helps to ensure that most of the extrusions are anchored to the lightning infill at the ends.
|
||||
// It requires modifying this instance of print object in a specific way, so that we do not invalidate the pointers in our surfaces_by_layer structure.
|
||||
if (has_lightning_infill) {
|
||||
@@ -3575,13 +3711,13 @@ static void clamp_feature_filament_to_valid(ConfigOptionInt &opt, size_t num_ext
|
||||
opt.value = 1;
|
||||
}
|
||||
|
||||
PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders)
|
||||
PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders, std::vector<int>& variant_index)
|
||||
{
|
||||
PrintObjectConfig config = default_object_config;
|
||||
{
|
||||
DynamicPrintConfig src_normalized(object.config.get());
|
||||
src_normalized.normalize_fdm();
|
||||
config.apply(src_normalized, true);
|
||||
update_static_print_config_from_dynamic(config, src_normalized, variant_index, print_options_with_variant, 1);
|
||||
}
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.support_filament, num_extruders);
|
||||
@@ -3609,7 +3745,7 @@ struct FeatureFilamentOverrideMask
|
||||
bool inner_wall_filament_id = false;
|
||||
};
|
||||
|
||||
static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPrintConfig &in, FeatureFilamentOverrideMask &feature_overrides)
|
||||
static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPrintConfig &in, FeatureFilamentOverrideMask &feature_overrides, std::vector<int>& variant_index)
|
||||
{
|
||||
// 1) Explicit feature filament values take precedence over base extruder fallback.
|
||||
auto *opt_extruder = in.opt<ConfigOptionInt>(key_extruder);
|
||||
@@ -3650,8 +3786,18 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr
|
||||
else if (it->first == "inner_wall_filament_id")
|
||||
feature_overrides.inner_wall_filament_id = false;
|
||||
}
|
||||
} else
|
||||
my_opt->set(it->second.get());
|
||||
} else {
|
||||
if (*my_opt != *(it->second)) {
|
||||
if (my_opt->is_scalar() || variant_index.empty() || (print_options_with_variant.find(it->first) == print_options_with_variant.end()))
|
||||
my_opt->set(it->second.get());
|
||||
//my_opt->set(it->second.get());
|
||||
else {
|
||||
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(my_opt);
|
||||
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(it->second.get());
|
||||
opt_vec_src->set_to_index(opt_vec_dest, variant_index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Apply base extruder only to features that were not explicitly overridden.
|
||||
@@ -3671,7 +3817,7 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr
|
||||
}
|
||||
}
|
||||
|
||||
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders)
|
||||
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders, std::vector<int>& variant_index)
|
||||
{
|
||||
PrintRegionConfig config = default_or_parent_region_config;
|
||||
FeatureFilamentOverrideMask feature_overrides;
|
||||
@@ -3689,17 +3835,17 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau
|
||||
if (volume.is_model_part()) {
|
||||
// default_or_parent_region_config contains the Print's PrintRegionConfig.
|
||||
// Override with ModelObject's PrintRegionConfig values.
|
||||
apply_to_print_region_config(config, volume.get_object()->config.get(), feature_overrides);
|
||||
apply_to_print_region_config(config, volume.get_object()->config.get(), feature_overrides, variant_index);
|
||||
} else {
|
||||
// default_or_parent_region_config contains parent PrintRegion config, which already contains ModelVolume's config.
|
||||
}
|
||||
apply_to_print_region_config(config, volume.config.get(), feature_overrides);
|
||||
apply_to_print_region_config(config, volume.config.get(), feature_overrides, variant_index);
|
||||
if (! volume.material_id().empty())
|
||||
apply_to_print_region_config(config, volume.material()->config.get(), feature_overrides);
|
||||
apply_to_print_region_config(config, volume.material()->config.get(), feature_overrides, variant_index);
|
||||
if (layer_range_config != nullptr) {
|
||||
// Not applicable to modifiers.
|
||||
assert(volume.is_model_part());
|
||||
apply_to_print_region_config(config, *layer_range_config, feature_overrides);
|
||||
apply_to_print_region_config(config, *layer_range_config, feature_overrides, variant_index);
|
||||
}
|
||||
// Resolve feature defaults and clamp invalid extruders to index 1.
|
||||
clamp_feature_filament_to_valid(config.sparse_infill_filament_id, num_extruders);
|
||||
@@ -3749,7 +3895,7 @@ void PrintObject::update_slicing_parameters()
|
||||
}
|
||||
|
||||
// Orca: XYZ shrinkage compensation has introduced the const Vec3d &object_shrinkage_compensation parameter to the function below
|
||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation)
|
||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z, const Vec3d &object_shrinkage_compensation, std::vector<int> variant_index)
|
||||
{
|
||||
PrintConfig print_config;
|
||||
PrintObjectConfig object_config;
|
||||
@@ -3759,14 +3905,14 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full
|
||||
default_region_config.apply(full_config, true);
|
||||
// BBS
|
||||
size_t filament_extruders = print_config.filament_diameter.size();
|
||||
object_config = object_config_from_model_object(object_config, model_object, filament_extruders);
|
||||
object_config = object_config_from_model_object(object_config, model_object, filament_extruders, variant_index);
|
||||
|
||||
std::vector<unsigned int> object_extruders;
|
||||
for (const ModelVolume* model_volume : model_object.volumes)
|
||||
if (model_volume->is_model_part()) {
|
||||
PrintRegion::collect_object_printing_extruders(
|
||||
print_config,
|
||||
region_config_from_model_volume(default_region_config, nullptr, *model_volume, filament_extruders),
|
||||
region_config_from_model_volume(default_region_config, nullptr, *model_volume, filament_extruders, variant_index),
|
||||
object_config.brim_type != btNoBrim && object_config.brim_width > 0.,
|
||||
object_extruders);
|
||||
for (const std::pair<const t_layer_height_range, ModelConfig> &range_and_config : model_object.layer_config_ranges)
|
||||
@@ -3778,7 +3924,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full
|
||||
range_and_config.second.has("bottom_surface_filament_id"))
|
||||
PrintRegion::collect_object_printing_extruders(
|
||||
print_config,
|
||||
region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, filament_extruders),
|
||||
region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, filament_extruders, variant_index),
|
||||
object_config.brim_type != btNoBrim && object_config.brim_width > 0.,
|
||||
object_extruders);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <exception>
|
||||
#include <cstdint>
|
||||
|
||||
#include "miniz_extension.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW64__)
|
||||
#include "boost/nowide/cstdio.hpp"
|
||||
@@ -15,6 +17,33 @@
|
||||
namespace Slic3r {
|
||||
|
||||
namespace {
|
||||
std::string decode_zip_unicode_path_extra_field(const std::string& extra, const std::string& path)
|
||||
{
|
||||
size_t offset = 0;
|
||||
const mz_uint32 path_crc = mz_crc32(0, reinterpret_cast<const unsigned char*>(path.data()), path.size());
|
||||
|
||||
while (offset + 4 <= extra.size()) {
|
||||
const unsigned char* field = reinterpret_cast<const unsigned char*>(extra.data() + offset);
|
||||
const std::uint16_t len = field[2] | (static_cast<std::uint16_t>(field[3]) << 8);
|
||||
if (offset + 4 + len > extra.size())
|
||||
break;
|
||||
|
||||
if (field[0] == 0x75 && field[1] == 0x70 && len >= 5 && field[4] == 0x01) {
|
||||
const mz_uint32 stored_crc =
|
||||
static_cast<mz_uint32>(field[5]) |
|
||||
(static_cast<mz_uint32>(field[6]) << 8) |
|
||||
(static_cast<mz_uint32>(field[7]) << 16) |
|
||||
(static_cast<mz_uint32>(field[8]) << 24);
|
||||
if (stored_crc == path_crc)
|
||||
return std::string(extra.data() + offset + 9, extra.data() + offset + 4 + len);
|
||||
}
|
||||
|
||||
offset += 4 + len;
|
||||
}
|
||||
|
||||
return Slic3r::decode_path(path.c_str());
|
||||
}
|
||||
|
||||
bool open_zip(mz_zip_archive *zip, const char *fname, bool isread)
|
||||
{
|
||||
if (!zip) return false;
|
||||
@@ -76,6 +105,16 @@ bool open_zip_writer(mz_zip_archive *zip, const std::string &fname)
|
||||
bool close_zip_reader(mz_zip_archive *zip) { return close_zip(zip, true); }
|
||||
bool close_zip_writer(mz_zip_archive *zip) { return close_zip(zip, false); }
|
||||
|
||||
std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat)
|
||||
{
|
||||
if (stat.m_is_utf8)
|
||||
return stat.m_filename;
|
||||
|
||||
std::string extra(1024, 0);
|
||||
const size_t extra_size = mz_zip_reader_get_extra(zip, stat.m_file_index, extra.data(), extra.size());
|
||||
return decode_zip_unicode_path_extra_field(extra.substr(0, extra_size > 0 ? extra_size - 1 : 0), stat.m_filename);
|
||||
}
|
||||
|
||||
MZ_Archive::MZ_Archive()
|
||||
{
|
||||
mz_zip_zero_struct(&arch);
|
||||
|
||||
@@ -10,6 +10,7 @@ bool open_zip_reader(mz_zip_archive *zip, const std::string &fname_utf8);
|
||||
bool open_zip_writer(mz_zip_archive *zip, const std::string &fname_utf8);
|
||||
bool close_zip_reader(mz_zip_archive *zip);
|
||||
bool close_zip_writer(mz_zip_archive *zip);
|
||||
std::string decode_archive_entry_path(mz_zip_archive *zip, const mz_zip_archive_file_stat &stat);
|
||||
|
||||
class MZ_Archive {
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user