Merge branch 'main' into dev/ams-heat

This commit is contained in:
Noisyfox
2026-07-13 14:43:45 +08:00
committed by GitHub
125 changed files with 6293 additions and 1534 deletions
@@ -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
+1
View File
@@ -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
+12
View File
@@ -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
+1
View File
@@ -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);
}
+27
View File
@@ -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
+61 -2
View File
@@ -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 = &region_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);
+3 -2
View File
@@ -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);
}
}
+2
View File
@@ -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).");
+54 -36
View File
@@ -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());
+31 -8
View File
@@ -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 &params, 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);
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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;
+2 -1
View File
@@ -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
+13 -1
View File
@@ -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();
+5
View File
@@ -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)
+62 -18
View File
@@ -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 &lt : 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;
};
+13 -10
View File
@@ -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);
}
}
+3
View File
@@ -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;
+4
View File
@@ -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
View File
@@ -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();
+16
View File
@@ -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) {
+23
View File
@@ -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
View File
@@ -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",
+4
View File
@@ -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"
+2 -2
View File
@@ -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();
+17 -12
View File
@@ -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
View File
@@ -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)
+58 -1
View File
@@ -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
View File
@@ -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);
}
+39
View File
@@ -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);
+1
View File
@@ -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: