Merge remote-tracking branch 'upstream/main' into dev/h2d

# Conflicts:
#	resources/profiles/BBL.json
#	src/slic3r/GUI/AmsMappingPopup.cpp
#	src/slic3r/GUI/MediaFilePanel.cpp
#	src/slic3r/GUI/Plater.cpp
#	src/slic3r/GUI/StatusPanel.cpp
This commit is contained in:
Noisyfox
2025-09-16 11:27:44 +08:00
6267 changed files with 210560 additions and 178856 deletions

View File

@@ -21,6 +21,197 @@
namespace Slic3r {
// Calculate infill rotation angle (in radians) for a given layer from a rotation template.
// Grammar subset handled (rotation only):
// [±]α[*Z or !][joint][-][N|B|T][length][* or !]
// [±]α* sets an initial angle only (no layer processed)
// Where:
// - α: angle in degrees. Without a sign it's absolute; with +/ it's relative. α% means a percentage of 360°.
// - Runtime: *Z repeats the instruction Z times; bare * is a no-op used for initialization; ! runs once globally and then stops.
// - Solid signs (D,S,O,M,R) are not processed here; if present they are treated as invalid/non-rotation characters.
// - Joint signs (shape of the turn across a range):
// / linear;
// N,n vertical sinus (n = lazy/half amplitude);
// Z,z horizontal sinus (z = lazy/half amplitude);
// $ arcsin; L quarter circle H→V; l quarter circle V→H;
// U,u squared; Q,q cubic; ~ random; ^ pseudorandom; | middle step; # vertical step at end.
// - Counting / range length:
// After the joint (or after α) a count determines duration of the turn:
// N = layer count, B = bottom_shell_layers, T = top_shell_layers.
// Prefix '-' flips the joint (swap initial/final orientation).
// - Length modifiers convert the count to a Z range instead of a pure layer count:
// mm, cm, m, ' (feet), " (inches), # (standard height of N layers), % (percent of model height).
//
// Behavior:
// - The template string is tokenized by commas/whitespace and evaluated cyclically with one or more "ranges" per token.
// - Absolute α resets the accumulated angle at the start of its range; relative α accumulates.
// - *Z and ! control repetition and one-time execution of tokens across layers.
// - If the template contains no metalanguage symbols, it is treated as a simple comma-separated list of angles repeated by modulo.
// - Returns angle in radians for the requested layer_id. 0° aligns with +X; fillers may internally rotate as needed.
double calculate_infill_rotation_angle(const PrintObject* object,
size_t layer_id,
const double& fixed_infill_angle,
const std::string& template_string)
{
if (template_string.empty()) {
return Geometry::deg2rad(fixed_infill_angle);
}
double angle = 0.0;
ConfigOptionFloats rotate_angles;
const std::string search_string = "/NnZz$LlUuQq~^|#";
if (regex_search(template_string, std::regex("[+\\-%*@\'\"cm" + search_string + "]"))) { // template metalanguage of rotating infill
std::regex del("[\\s,]+");
std::sregex_token_iterator it(template_string.begin(), template_string.end(), del, -1);
std::vector<std::string> tk;
std::sregex_token_iterator end;
while (it != end) {
tk.push_back(*it++);
}
int t = 0;
int repeats = 0;
double angle_add = 0;
double angle_steps = 1;
double angle_start = 0;
double limit_fill_z = object->get_layer(0)->bottom_z();
double start_fill_z = limit_fill_z;
bool _noop = false;
auto fill_form = std::string::npos;
bool _absolute = false;
bool _negative = false;
std::vector<bool> stop(tk.size(), false);
for (int i = 0; i <= layer_id; i++) {
double fill_z = object->get_layer(i)->bottom_z();
if (limit_fill_z < object->get_layer(i)->slice_z) {
if (repeats) { // if repeats >0 then restore parameters for new iteration
limit_fill_z += limit_fill_z - start_fill_z;
start_fill_z = fill_z;
repeats--;
} else {
start_fill_z = fill_z;
limit_fill_z = object->get_layer(i)->print_z;
// Solid handling removed: this function only computes rotation.
fill_form = std::string::npos;
do {
if (!stop[t]) {
_noop = false;
_absolute = false;
_negative = false;
angle_start += angle_add;
angle_add = 0;
angle_steps = 1;
repeats = 1;
if (tk[t].find('!') != std::string::npos) // this is an one-time instruction
stop[t] = true;
char* cs = &tk[t][0];
if ((cs[0] >= '0' && cs[0] <= '9') && !(cs[0] == '+' || cs[0] == '-')) // absolute/relative
_absolute = true;
angle_add = strtod(cs, &cs); // read angle parameter
if (cs[0] == '%') { // percentage of angles
angle_add *= 3.6;
cs = &cs[1];
}
int tit = tk[t].find('*');
if (tit != std::string::npos) // overall angle_cycles
repeats = strtol(&tk[t][tit + 1], &cs, 0);
if (repeats) { // run if overall cycles greater than 0
// Solid signs (D,S,O,M,R) are not handled here; if present they behave as invalid characters.
if (cs[0] == 'B') {
angle_steps = object->print()->default_region_config().bottom_shell_layers.value;
} else if (cs[0] == 'T') {
angle_steps = object->print()->default_region_config().top_shell_layers.value;
} else {
fill_form = search_string.find(cs[0]);
if (fill_form != std::string::npos)
cs = &cs[1];
_negative = (cs[0] == '-'); // negative parameter
angle_steps = abs(strtod(cs, &cs));
if (angle_steps && cs[0] != '\0' && cs[0] != '!') {
if (cs[0] == '%') // value in the percents of fill_z
limit_fill_z = angle_steps * object->height() * 1e-8;
else if (cs[0] == '#') // value in the feet
limit_fill_z = angle_steps * object->config().layer_height;
else if (cs[0] == '\'') // value in the feet
limit_fill_z = angle_steps * 12 * 25.4;
else if (cs[0] == '\"') // value in the inches
limit_fill_z = angle_steps * 25.4;
else if (cs[0] == 'c') // value in centimeters
limit_fill_z = angle_steps * 10.;
else if (cs[0] == 'm') {
if (cs[1] == 'm') { // value in the millimeters
limit_fill_z = angle_steps * 1.;
} else{
limit_fill_z = angle_steps * 1000.;
}
}
limit_fill_z += fill_z;
angle_steps = 0; // limit_fill_z has already count
}
}
if (angle_steps) { // if limit_fill_z does not setting by lenght method. Get count the layer id above model height
if (fill_form == std::string::npos && !_absolute)
angle_add *= (int) angle_steps;
int idx = i + std::max(angle_steps - 1, 0.);
int sdx = std::max(0, idx - (int) object->layers().size());
idx = std::min(idx, (int) object->layers().size() - 1);
limit_fill_z = object->get_layer(idx)->print_z + sdx * object->config().layer_height;
}
repeats = std::max(--repeats, 0);
} else
_noop = true; // set the dumb cycle
if (_absolute) { // is absolute
angle_start = angle_add;
angle_add = 0;
}
}
if (++t >= tk.size())
t = 0;
} while (std::all_of(stop.begin(), stop.end(), [](bool v) { return v; }) ?
false :
(t ? _noop : false) || stop[t]); // if this is a dumb instruction which never reaprated twice
}
}
double top_z = object->get_layer(i)->print_z;
double negvalue = (_negative ? limit_fill_z - top_z : top_z - start_fill_z) / (limit_fill_z - start_fill_z);
switch (fill_form) {
case 0: break; // /-joint, linear
case 1: negvalue -= sin(negvalue * PI * 2.) / (PI * 2.); break; // N-joint, sinus, vertical start
case 2: negvalue -= sin(negvalue * PI * 2.) / (PI * 4.); break; // n-joint, sinus, vertical start, lazy
case 3: negvalue += sin(negvalue * PI * 2.) / (PI * 2.); break; // Z-joint, sinus, horizontal start
case 4: negvalue += sin(negvalue * PI * 2.) / (PI * 4.); break; // z-joint, sinus, horizontal start, lazy
case 5: negvalue = asin(negvalue * 2. - 1.) / PI + 0.5; break; // $-joint, arcsin
case 6: negvalue = sin(negvalue * PI / 2.); break; // L-joint, quarter of circle, horizontal start
case 7: negvalue = 1. - cos(negvalue * PI / 2.); break; // l-joint, quarter of circle, vertical start
case 8: negvalue = 1. - pow(1. - negvalue, 2); break; // U-joint, squared, x2
case 9: negvalue = pow(1 - negvalue, 2); break; // u-joint, squared, x2 inverse
case 10: negvalue = 1. - pow(1. - negvalue, 3); break; // Q-joint, cubic, x3
case 11: negvalue = pow(1. - negvalue, 3); break; // q-joint, cubic, x3 inverse
case 12: negvalue = (double) rand() / RAND_MAX; break; // ~-joint, random, fill the whole angle
case 13: negvalue += (double) rand() / RAND_MAX - 0.5; break; // ^-joint, pseudorandom, disperse at middle line
case 14: negvalue = 0.5; break; // |-joint, like #-joint but placed at middle angle
case 15: negvalue = _negative ? 0. : 1.; break; // #-joint, vertical at the end angle
}
angle = Geometry::deg2rad(angle_start + angle_add * negvalue);
}
} else {
rotate_angles.deserialize(template_string);
auto rotate_angle_idx = layer_id % rotate_angles.size();
angle = Geometry::deg2rad(rotate_angles.values[rotate_angle_idx]);
}
return angle;
}
struct SurfaceFillParams
{
// Zero based extruder ID.
@@ -35,6 +226,8 @@ struct SurfaceFillParams
coordf_t overlap = 0.;
// Angle as provided by the region config, in radians.
float angle = 0.f;
// Orca: is_using_template_angle
bool is_using_template_angle = false;
// Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set.
bool bridge;
// Non-negative for a bridge.
@@ -90,6 +283,7 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(spacing);
RETURN_COMPARE_NON_EQUAL(overlap);
RETURN_COMPARE_NON_EQUAL(angle);
RETURN_COMPARE_NON_EQUAL(is_using_template_angle);
RETURN_COMPARE_NON_EQUAL(density);
RETURN_COMPARE_NON_EQUAL(multiline);
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
@@ -118,6 +312,7 @@ struct SurfaceFillParams
this->spacing == rhs.spacing &&
this->overlap == rhs.overlap &&
this->angle == rhs.angle &&
this->is_using_template_angle == rhs.is_using_template_angle &&
this->bridge == rhs.bridge &&
this->bridge_angle == rhs.bridge_angle &&
this->density == rhs.density &&
@@ -627,7 +822,6 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
flow_params.insert({flow, {exp}});
else
it->second.push_back(exp);
it++;
};
auto append_density_param = [](std::map<float, ExPolygons> &density_params, float density, const ExPolygon &exp) {
@@ -636,7 +830,6 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
density_params.insert({density, {exp}});
else
it->second.push_back(exp);
it++;
};
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
@@ -652,11 +845,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
params.extruder = layerm.region().extruder(extrusion_role);
params.pattern = region_config.sparse_infill_pattern.value;
params.density = float(region_config.sparse_infill_density);
params.multiline = int(region_config.fill_multiline);
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.angle = 0.;
if (params.pattern == ipLockedZag) {
params.infill_lock_depth = scale_(region_config.infill_lock_depth);
params.skin_infill_depth = scale_(region_config.skin_infill_depth);
@@ -704,17 +895,24 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
params.extrusion_role = erSolidInfill;
}
}
// Orca: apply fill multiline only for sparse infill
params.multiline = params.extrusion_role == erInternalInfill ? int(region_config.fill_multiline) : 1;
if (params.extrusion_role == erInternalInfill) {
params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.infill_direction.value,
region_config.sparse_infill_rotate_template.value);
params.is_using_template_angle = !region_config.sparse_infill_rotate_template.value.empty();
} else {
params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.solid_infill_direction.value,
region_config.solid_infill_rotate_template.value);
params.is_using_template_angle = !region_config.solid_infill_rotate_template.value.empty();
}
params.bridge_angle = float(surface.bridge_angle);
if (region_config.align_infill_direction_to_model) {
auto m = layer.object()->trafo().matrix();
params.angle += atan2((float) m(1, 0), (float) m(0, 0));
}
if (params.extrusion_role == erInternalInfill) {
params.angle += float(Geometry::deg2rad(region_config.infill_direction.value));
} else {
params.angle += float(Geometry::deg2rad(region_config.solid_infill_direction.value));
}
// Calculate the actual flow we'll be using for this infill.
params.bridge = is_bridge || Fill::use_bridge_flow(params.pattern);
@@ -892,8 +1090,12 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
params.pattern = ipRectilinear;
params.density = 100.f;
params.extrusion_role = erSolidInfill;
params.angle = float(Geometry::deg2rad(layerm.region().config().solid_infill_direction.value));
// calculate the actual flow we'll be using for this infill
const PrintRegionConfig &region_config = layerm.region().config();
params.angle = calculate_infill_rotation_angle(layer.object(), layer.id(), region_config.solid_infill_direction.value,
region_config.solid_infill_rotate_template.value);
params.is_using_template_angle = !region_config.solid_infill_rotate_template.value.empty();
// calculate the actual flow we'll be using for this infill
params.flow = layerm.flow(frSolidInfill);
params.spacing = params.flow.spacing();
surface_fills.emplace_back(params);
@@ -996,6 +1198,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
f->layer_id = this->id();
f->z = this->print_z;
f->angle = surface_fill.params.angle;
f->is_using_template_angle = surface_fill.params.is_using_template_angle;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
f->print_config = &this->object()->print()->config();
f->print_object_config = &this->object()->config();
@@ -1054,184 +1257,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.config = &region_config;
params.pattern = surface_fill.params.pattern;
ConfigOptionFloats rotate_angles;
const std::string search_string = "/NnZz$LlUuQq~^|#";
std::string v(params.extrusion_role == erInternalInfill ? region_config.sparse_infill_rotate_template.value :
region_config.solid_infill_rotate_template.value);
if (regex_search(v, std::regex("[+\\-%*@\'\"cmSODMR" + search_string + "]"))) { // template metalanguage of rotating infill
std::regex del("[\\s,]+");
std::sregex_token_iterator it(v.begin(), v.end(), del, -1);
std::vector<std::string> tk;
std::sregex_token_iterator end;
while (it != end) {
tk.push_back(*it++);
}
int t = 0;
int repeats = 0;
double angle = 0;
double angle_add = 0;
double angle_steps = 1;
double angle_start = 0;
double limit_fill_z = this->object()->get_layer(0)->bottom_z();
double start_fill_z = limit_fill_z;
bool _noop = false;
auto solid = std::string::npos; // -1 - sparse, 0 - native (D), 1 - internal solid (S), 2 - concentric (O), 3 - monotonic (M), 4 - rectilinear (R)
auto fill_form = std::string::npos;
bool _absolute = false;
bool _negative = false;
std::vector<bool> stop(tk.size(), false);
for (int i = 0; i <= this->id(); i++) {
double fill_z = this->object()->get_layer(i)->bottom_z();
if (limit_fill_z < this->object()->get_layer(i)->slice_z) {
if (repeats) { // if repeats >0 then restore parameters for new iteration
limit_fill_z += limit_fill_z - start_fill_z;
start_fill_z = fill_z;
repeats--;
} else {
start_fill_z = fill_z;
limit_fill_z = this->object()->get_layer(i)->print_z;
solid = std::string::npos;
fill_form = std::string::npos;
do {
if (!stop[t]) {
_noop = false;
_absolute = false;
_negative = false;
angle_start += angle_add;
angle_add = 0;
angle_steps = 1;
repeats = 1;
if (tk[t].find('!') != std::string::npos) // this is an one-time instruction
stop[t] = true;
char* cs = &tk[t][0];
if ((cs[0] >= '0' && cs[0] <= '9') && !(cs[0] == '+' || cs[0] == '-')) // absolute/relative
_absolute = true;
angle_add = strtod(cs, &cs); // read angle parameter
if (cs[0] == '%') { // percentage of angles
angle_add *= 3.6;
cs = &cs[1];
}
int tit = tk[t].find('*');
if (tit != std::string::npos) // overall angle_cycles
repeats = strtol(&tk[t][tit + 1], &cs, 0);
if (repeats) { // run if overall cycles greater than 0
solid = std::string("DSOMR").find(cs[0]); // solid infill
if (solid != std::string::npos)
cs = &cs[1];
if (cs[0] == 'B') {
angle_steps = this->object()->print()->default_region_config().bottom_shell_layers.value;
} else if (cs[0] == 'T') {
angle_steps = this->object()->print()->default_region_config().top_shell_layers.value;
} else {
fill_form = search_string.find(cs[0]);
if (fill_form != std::string::npos)
cs = &cs[1];
_negative = (cs[0] == '-'); // negative parameter
angle_steps = abs(strtod(cs, &cs));
if (angle_steps && cs[0] != '\0' && cs[0] != '!') {
if (cs[0] == '%') // value in the percents of fill_z
limit_fill_z = angle_steps * this->object()->height() * 1e-8;
else if (cs[0] == '#') // value in the feet
limit_fill_z = angle_steps * this->object()->config().layer_height;
else if (cs[0] == '\'') // value in the feet
limit_fill_z = angle_steps * 12 * 25.4;
else if (cs[0] == '\"') // value in the inches
limit_fill_z = angle_steps * 25.4;
else if (cs[0] == 'c') // value in centimeters
limit_fill_z = angle_steps * 10.;
else if (cs[0] == 'm')
if (cs[1] == 'm') { // value in the millimeters
limit_fill_z = angle_steps * 1.;
} else // value in the meters
limit_fill_z = angle_steps * 1000.;
limit_fill_z += fill_z;
angle_steps = 0; // limit_fill_z has already count
}
}
if (angle_steps) { // if limit_fill_z does not setting by lenght method. Get count the layer id above model height
if (fill_form == std::string::npos && !_absolute)
angle_add *= (int) angle_steps;
int idx = i + std::max(angle_steps - 1, 0.);
int sdx = std::max(0, idx - (int) this->object()->layers().size());
idx = std::min(idx, (int) this->object()->layers().size() - 1);
limit_fill_z = this->object()->get_layer(idx)->print_z + sdx * this->object()->config().layer_height;
}
repeats = std::max(--repeats, 0);
} else
_noop = true; // set the dumb cycle
if (_absolute) { // is absolute
angle_start = angle_add;
angle_add = 0;
}
}
if (++t >= tk.size())
t = 0;
} while (std::all_of(stop.begin(), stop.end(), [](bool v) { return v; }) ? false :
(t ? _noop : false) || stop[t]); // if this is a dumb instruction which never reaprated twice
}
}
double top_z = this->object()->get_layer(i)->print_z;
double negvalue = (_negative ? limit_fill_z - top_z : top_z - start_fill_z) / (limit_fill_z - start_fill_z);
switch (fill_form) {
case 0: break; // /-joint, linear
case 1: negvalue -= sin(negvalue * PI * 2.) / (PI * 2.); break; // N-joint, sinus, vertical start
case 2: negvalue -= sin(negvalue * PI * 2.) / (PI * 4.); break; // n-joint, sinus, vertical start, lazy
case 3: negvalue += sin(negvalue * PI * 2.) / (PI * 2.); break; // Z-joint, sinus, horizontal start
case 4: negvalue += sin(negvalue * PI * 2.) / (PI * 4.); break; // z-joint, sinus, horizontal start, lazy
case 5: negvalue = asin(negvalue * 2. - 1.) / PI + 0.5; break; // $-joint, arcsin
case 6: negvalue = sin(negvalue * PI / 2.); break; // L-joint, quarter of circle, horizontal start
case 7: negvalue = 1. - cos(negvalue * PI / 2.); break; // l-joint, quarter of circle, vertical start
case 8: negvalue = 1. - pow(1. - negvalue, 2); break; // U-joint, squared, x2
case 9: negvalue = pow(1 - negvalue, 2); break; // u-joint, squared, x2 inverse
case 10: negvalue = 1. - pow(1. - negvalue, 3); break; // Q-joint, cubic, x3
case 11: negvalue = pow(1. - negvalue, 3); break; // q-joint, cubic, x3 inverse
case 12: negvalue = (double) rand() / RAND_MAX; break; // ~-joint, random, fill the whole angle
case 13: negvalue += (double) rand() / RAND_MAX - 0.5; break; // ^-joint, pseudorandom, disperse at middle line
case 14: negvalue = 0.5; break; // |-joint, like #-joint but placed at middle angle
case 15: negvalue = _negative ? 0. : 1.; break; // #-joint, vertical at the end angle
}
angle = angle_start + angle_add * negvalue;
}
if (solid != std::string::npos) {
switch (solid) {
case 1: params.pattern = region_config.internal_solid_infill_pattern.value; break; // selected solid pattern
case 2: params.pattern = ipConcentric; break; // concentric pattern
case 3: params.pattern = ipMonotonic; break; // monotonic pattern
case 4: params.pattern = ipRectilinear; // rectilinear pattern
} // or else use native pattern
params.extrusion_role = erSolidInfill;
params.density = 1.;
surface_fill.params.pattern = params.pattern;
f = std::unique_ptr<Fill>(Fill::new_from_type(params.pattern)); // reinitialize surface
f->set_bounding_box(bbox);
f->layer_id = this->id();
f->z = this->print_z;
f->angle = surface_fill.params.angle;
f->print_config = &this->object()->print()->config();
f->print_object_config = &this->object()->config();
params.use_arachne = surface_fill.params.pattern == ipConcentric || surface_fill.params.pattern == ipConcentricInternal;
}
f->rotate_angle = Geometry::deg2rad(angle);
} else {
rotate_angles.deserialize(v);
auto rotate_angle_idx = f->layer_id % rotate_angles.size();
f->rotate_angle = Geometry::deg2rad(rotate_angles.values[rotate_angle_idx]);
}
if( surface_fill.params.pattern == ipLockedZag ) {
if( surface_fill.params.pattern == ipLockedZag ) {
params.locked_zag = true;
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
params.skin_infill_depth = surface_fill.params.skin_infill_depth;
@@ -1293,7 +1319,23 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != nullptr);
#endif
}
/**
* Generate sparse-infill polylines for anchoring/analysis purposes.
*
* This produces the geometric polylines of internal sparse infill for the current
* layer (using the same infill pattern, angle, rotation template, and spacing that
* normal slicing would use), but it does not create extrusion entities.
*
* The returned polylines are consumed by internal-bridge detection on the next
* layer to derive anchor lines and compute the bridge direction over sparse infill.
*
* Notes:
* - Only `stInternal` surfaces are considered.
* - Rotation templates (e.g. `sparse_infill_rotate_template`) are applied so the
* anchors reflect the actual infill orientation.
* - For lightning/adaptive patterns, the respective generators are wired so their
* polylines match the final infill layout.
*/
Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator) const
{
LockRegionParam skin_inner_param;
@@ -1346,6 +1388,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers.
f->z = this->print_z;
f->angle = surface_fill.params.angle;
f->is_using_template_angle = surface_fill.params.is_using_template_angle;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
f->print_config = &this->object()->print()->config();
f->print_object_config = &this->object()->config();

View File

@@ -15,8 +15,9 @@ public:
Fill* clone() const override { return new Fill3DHoneycomb(*this); };
~Fill3DHoneycomb() override {}
// require bridge flow since most of this pattern hangs in air
bool use_bridge_flow() const override { return true; }
// note: updated 3D Honeycomb doesn't need bridge flow because the
// pattern is placed on top of previous layers
bool use_bridge_flow() const override { return false; }
bool is_self_crossing() override { return false; }
protected:

View File

@@ -306,7 +306,9 @@ std::pair<float, Point> Fill::_infill_direction(const Surface *surface) const
out_angle = float(surface->bridge_angle);
} else if (this->layer_id != size_t(-1)) {
// alternate fill direction
out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers);
//Orca: if template angle is not empty, don't apply layer angle
if(!is_using_template_angle)
out_angle += this->_layer_angle(this->layer_id / surface->thickness_layers);
} else {
// printf("Layer_ID undefined!\n");
}

View File

@@ -119,8 +119,8 @@ public:
coordf_t overlap;
// in radians, ccw, 0 = East
float angle;
// Orca: enable angle shifting for layer change
float rotate_angle{ M_PI/180.0 };
// Orca: is_using_template_angle
bool is_using_template_angle{false};
// In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines.
// Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic.
// If left to zero, the links will not be limited.
@@ -182,7 +182,6 @@ protected:
overlap(0.),
// Initial angle is undefined.
angle(FLT_MAX),
rotate_angle(M_PI/180.0),
link_max_length(0),
loop_clipping(0),
// The initial bounding box is empty, therefore undefined.
@@ -204,7 +203,7 @@ protected:
ExPolygon expolygon,
ThickPolylines& thick_polylines_out) {}
virtual float _layer_angle(size_t idx) const { return rotate_angle; }
virtual float _layer_angle(size_t idx) const { return is_using_template_angle ? 0.f : (idx & 1) ? float(M_PI/2.) : 0.f; }
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;

View File

@@ -3044,7 +3044,7 @@ Polylines FillRectilinear::fill_surface(const Surface *surface, const FillParams
{
Polylines polylines_out;
// Orca Todo: fow now don't use fill_surface_by_multilines for zipzag infill
if (params.full_infill() || params.pattern == ipCrossZag || params.pattern == ipZigZag || params.pattern == ipLockedZag) {
if (params.full_infill() || params.multiline == 1 || params.pattern == ipCrossZag || params.pattern == ipZigZag || params.pattern == ipLockedZag) {
if (!fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out))
BOOST_LOG_TRIVIAL(error) << "FillRectilinear::fill_surface() fill_surface_by_lines() failed to fill a region.";
} else {

View File

@@ -7,7 +7,6 @@
#include <unordered_set>
#include <utility>
#include <tbb/parallel_for.h>
#include <mutex>
namespace Slic3r {
@@ -157,8 +156,8 @@ vector<double> getGridValues(int i, int j, vector<vector<double>>& data)
values.push_back(data[i][j]);
return values;
}
bool needContour(double value, double contourValue) { return value >= contourValue; }
Point interpolate(std::vector<std::vector<MarchingSquares::Point>>& posxy,
static bool needContour(double value, double contourValue) { return value >= contourValue; }
static Point interpolate(std::vector<std::vector<MarchingSquares::Point>>& posxy,
std::vector<int> p1ij,
std::vector<int> p2ij,
double v1,
@@ -186,7 +185,7 @@ Point interpolate(std::vector<std::vector<MarchingSquares::Point>>& posxy,
return p;
}
void process_block(int i,
static void process_block(int i,
int j,
vector<vector<double>>& data,
double contourValue,
@@ -288,43 +287,7 @@ void process_block(int i,
}
}
// --- Chaikin Smooth ---
static Polyline chaikin_smooth(Polyline poly, int iterations , double weight )
{
if (poly.points.size() < 3) return poly;
const double w1 = 1.0 - weight;
decltype(poly.points) buffer;
buffer.reserve(poly.points.size() * 2);
for (int it = 0; it < iterations; ++it) {
buffer.clear();
buffer.push_back(poly.points.front());
for (size_t i = 0; i < poly.points.size() - 1; ++i) {
const auto &p0 = poly.points[i];
const auto &p1 = poly.points[i + 1];
buffer.emplace_back(
p0.x() * w1 + p1.x() * weight,
p0.y() * w1 + p1.y() * weight
);
buffer.emplace_back(
p0.x() * weight + p1.x() * w1,
p0.y() * weight + p1.y() * w1
);
}
buffer.push_back(poly.points.back());
poly.points.swap(buffer);
}
return poly;
}
void drawContour(double contourValue,
static void drawContour(double contourValue,
int gridSize_w,
int gridSize_h,
vector<vector<double>>& data,
@@ -382,62 +345,18 @@ void drawContour(double contourValue,
for (myPoint& pt : p) {
repltmp.points.push_back(Slic3r::Point(pt.x, pt.y));
}
// symplify tolerance based on density
const float min_tolerance = 0.005f;
const float max_tolerance = 0.2f;
float simplify_tolerance = (0.005f / params.density);
simplify_tolerance = std::clamp(simplify_tolerance, min_tolerance, max_tolerance);
repltmp.simplify(scale_(simplify_tolerance));
repltmp = chaikin_smooth(repltmp, 2, 0.25);
repls.push_back(repltmp);
}
}
} // namespace MarchingSquares
static float sin_table[360];
static float cos_table[360];
static std::once_flag trig_tables_once_flag;
#define PIratio 57.29577951308232 // 180/PI
static void initialize_lookup_tables()
{
for (int i = 0; i < 360; ++i) {
float angle = i * (M_PI / 180.0);
sin_table[i] = std::sin(angle);
cos_table[i] = std::cos(angle);
}
}
inline static void ensure_trig_tables_initialized()
{
std::call_once(trig_tables_once_flag, initialize_lookup_tables);
}
inline static float get_sin(float angle)
{
angle = angle * PIratio;
int index = static_cast<int>(std::fmod(angle, 360) + 360) % 360;
return sin_table[index];
}
inline static float get_cos(float angle)
{
angle = angle * PIratio;
int index = static_cast<int>(std::fmod(angle, 360) + 360) % 360;
return cos_table[index];
}
void FillTpmsFK::_fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
Polylines& polylines_out)
{
ensure_trig_tables_initialized();
auto infill_angle = float(this->angle + (CorrectionAngle * 2 * M_PI) / 360.);
if(std::abs(infill_angle) >= EPSILON)
expolygon.rotate(-infill_angle);
@@ -452,40 +371,29 @@ void FillTpmsFK::_fill_surface_single(const FillParams& params,
float xlen = boxsize.x();
float ylen = boxsize.y();
const float delta = 0.5f; // mesh step (adjust for quality/performance)
const float delta = 0.4f; // mesh step (adjust for quality/performance)
float myperiod = 2 * PI / vari_T;
float c_z = myperiod * this->z; // z height
// scalar field Fischer-Koch
auto scalar_field = [&](float x, float y) {
float a_x = myperiod * x;
float b_y = myperiod * y;
auto scalar_field = [&](float x, float y) -> float {
const float a_x = myperiod * x;
const float b_y = myperiod * y;
// Fischer - Koch S equation:
// cos(2x)sin(y)cos(z) + cos(2y)sin(z)cos(x) + cos(2z)sin(x)cos(y) = 0
const float cos2ax = get_cos(2*a_x);
const float cos2by = get_cos(2*b_y);
const float cos2cz = get_cos(2*c_z);
const float sinby = get_sin(b_y);
const float cosax = get_cos(a_x);
const float sinax = get_sin(a_x);
const float cosby = get_cos(b_y);
const float sincz = get_sin(c_z);
const float coscz = get_cos(c_z);
return cos2ax * sinby * coscz
+ cos2by * sincz * cosax
+ cos2cz * sinax * cosby;
return cosf(2 * a_x) * sinf(b_y) * cosf(c_z)
+ cosf(2 * b_y) * sinf(c_z) * cosf(a_x)
+ cosf(2 * c_z) * sinf(a_x) * cosf(b_y);
};
// Mesh generation
std::vector<std::vector<MarchingSquares::Point>> posxy;
int i = 0, j = 0;
for (float y = -(ylen) / 2.0f - 2; y < (ylen) / 2.0f + 2; y = y + delta, i++) {
for (float y = -(ylen) / 2.0f - 0.5f; y < (ylen) / 2.0f + 0.5f; y = y + delta, i++) {
j = 0;
std::vector<MarchingSquares::Point> colposxy;
for (float x = -(xlen) / 2.0f - 2; x < (xlen) / 2.0f + 2; x = x + delta, j++) {
for (float x = -(xlen) / 2.0f - 0.5f; x < (xlen) / 2.0f + 0.5f; x = x + delta, j++) {
MarchingSquares::Point pt;
pt.x = cenpos.x() + x;
pt.y = cenpos.y() + y;
@@ -510,33 +418,36 @@ void FillTpmsFK::_fill_surface_single(const FillParams& params,
Polylines polylines;
const double contour_value = 0.075; // offset from zero to avoid numerical issues
const double contour_value = 0; // offset from theoretical surface
MarchingSquares::drawContour(contour_value, width , height , data, posxy, polylines, params);
if (!polylines.empty()) {
// Apply multiline offset if needed
multiline_fill(polylines, params, spacing);
// Apply multiline offset if needed
multiline_fill(polylines, params, spacing);
polylines = intersection_pl(polylines, expolygon);
polylines = intersection_pl(polylines, expolygon);
// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
if (! polylines.empty()) {
// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
// The infill perimeter lines should be separated by around a single infill line width.
const double minlength = scale_(0.8 * this->spacing);
polylines.erase(
std::remove_if(polylines.begin(), polylines.end(), [minlength](const Polyline &pl) { return pl.length() < minlength; }),
polylines.end());
}
if (! polylines.empty()) {
// connect lines
size_t polylines_out_first_idx = polylines_out.size();
chain_or_connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
//chain_or_connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
//chain_infill not situable for this pattern due to internal "islands", this also affect performance a lot.
connect_infill(std::move(polylines), expolygon, polylines_out, this->spacing, params);
// new paths must be rotated back
if (std::abs(infill_angle) >= EPSILON) {
for (auto it = polylines_out.begin() + polylines_out_first_idx; it != polylines_out.end(); ++ it)
it->rotate(infill_angle);
}
}
}

View File

@@ -1129,7 +1129,7 @@ void PerimeterGenerator::process_classic()
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
coord_t ext_perimeter_spacing2;
// Orca: ignore precise_outer_wall if wall_sequence is not InnerOuter
if(config->precise_outer_wall)
if(config->precise_outer_wall && config->wall_sequence == WallSequence::InnerOuter)
ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.width() + this->perimeter_flow.width()));
else
ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing()));
@@ -2124,7 +2124,7 @@ void PerimeterGenerator::process_arachne()
if (is_topmost_layer && loop_number > 0 && config->only_one_wall_top)
loop_number = 0;
auto apply_precise_outer_wall = config->precise_outer_wall;
auto apply_precise_outer_wall = config->precise_outer_wall && config->wall_sequence == WallSequence::InnerOuter;
// Orca: properly adjust offset for the outer wall if precise_outer_wall is enabled.
ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution),
apply_precise_outer_wall? -float(ext_perimeter_width - ext_perimeter_spacing )

View File

@@ -788,7 +788,7 @@ static std::vector<std::string> s_Preset_print_options {
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction",
"seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density","fill_multiline", "sparse_infill_pattern", "lateral_lattice_angle_1", "lateral_lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern",
"infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density",
"align_infill_direction_to_model",
"align_infill_direction_to_model", "extra_solid_infills",
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target",
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_inset",
"support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing",

View File

@@ -1627,6 +1627,12 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
// }
// }
// check wall sequence and precise outer wall
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter) {
warning->string = L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences.");
warning->opt_key = "precise_outer_wall";
}
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
}

View File

@@ -679,7 +679,7 @@ void PrintConfigDef::init_common_params()
def = this->add("preferred_orientation", coFloat);
def->label = L("Preferred orientation");
def->tooltip = L("Automatically orient stls on the Z-axis upon initial import.");
def->tooltip = L("Automatically orient stls on the Z axis upon initial import.");
def->sidetext = "°"; // degrees, don't need translation
def->max = 360;
def->min = -360;
@@ -1203,9 +1203,10 @@ void PrintConfigDef::init_fff_params()
def = this->add("precise_outer_wall",coBool);
def->label = L("Precise wall");
def->category = L("Quality");
def->tooltip = L("Improve shell precision by adjusting outer wall spacing. This also improves layer consistency.");
def->set_default_value(new ConfigOptionBool{false});
def->tooltip = L("Improve shell precision by adjusting outer wall spacing. This also improves layer consistency. NOTE: This option "
"will be ignored for outer-inner or inner-outer-inner wall sequences.");
def->set_default_value(new ConfigOptionBool{true});
def = this->add("only_one_wall_top", coBool);
def->label = L("Only one wall on top surfaces");
def->category = L("Quality");
@@ -1454,9 +1455,9 @@ void PrintConfigDef::init_fff_params()
def = this->add("brim_ears_detection_length", coFloat);
def->label = L("Brim ear detection radius");
def->category = L("Support");
def->tooltip = L("The geometry will be decimated before detecting sharp angles. This parameter indicates the "
"minimum length of the deviation for the decimation. "
"\n0 to deactivate.");
def->tooltip = L("The geometry will be decimated before detecting sharp angles. "
"This parameter indicates the minimum length of the deviation for the decimation.\n"
"0 to deactivate.");
def->sidetext = "mm"; // milimeters, don't need translation
def->min = 0;
def->mode = comAdvanced;
@@ -2114,8 +2115,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("default_filament_colour", coStrings);
def->label = L("Default color");
def->tooltip = L("Default filament color"
"\nRight click to reset value to system default.");
def->tooltip = L("Default filament color.\n"
"Right click to reset value to system default.");
def->gui_type = ConfigOptionDef::GUIType::color;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings{""});
@@ -2567,6 +2568,13 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("extra_solid_infills", coString);
def->label = L("Insert solid layers");
def->category = L("Strength");
def->tooltip = L("Insert solid infill at specific layers. Use N to insert every Nth layer, N#K to insert K consecutive solid layers every N layers (K is optional, e.g. '5#' equals '5#1'), or a comma-separated list (e.g. 1,7,9) to insert at explicit layers. Layers are 1-based.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString());
// Infill multiline
def = this->add("fill_multiline", coInt);
@@ -2816,7 +2824,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("default_junction_deviation", coFloat);
def->label = L("Junction Deviation");
def->tooltip = L("Marlin Firmware Junction Deviation (replaces the traditional XY Jerk setting)");
def->tooltip = L("Marlin Firmware Junction Deviation (replaces the traditional XY Jerk setting).");
def->sidetext = "mm"; // milimeters, don't need translation
def->min = 0;
def->mode = comAdvanced;
@@ -3044,7 +3052,7 @@ void PrintConfigDef::init_fff_params()
"This is the fast and straight algorithm without unnecessary nozzle shake that gives a smooth pattern. "
"But it is more useful for forming loose walls in the entire they array.\n"
"Combined: Joint mode [Displacement] + [Extrusion]. The appearance of the walls is similar to [Displacement] Mode, but it leaves no pores between the perimeters.\n\n"
"Attention! The [Extrusion] and [Combined] modes works only the fuzzy_skin_thickness parameter not more than the thickness of printed loop."
"Attention! The [Extrusion] and [Combined] modes works only the fuzzy_skin_thickness parameter not more than the thickness of printed loop. "
"At the same time, the width of the extrusion for a particular layer should also not be below a certain level. "
"It is usually equal 15-25%% of a layer height. Therefore, the maximum fuzzy skin thickness with a perimeter width of 0.4 mm and a layer height of 0.2 mm will be 0.4-(0.2*0.25)=±0.35mm! "
"If you enter a higher parameter than this, the error Flow::spacing() will displayed, and the model will not be sliced. You can choose this number until this error is repeated." );
@@ -3366,35 +3374,37 @@ void PrintConfigDef::init_fff_params()
//Orca
def = this->add("sparse_infill_rotate_template", coString);
def->label = L("Sparse infill rotatation template");
def->label = L("Sparse infill rotation template");
def->category = L("Strength");
def->tooltip = L("This parameter adds a rotation of sparse infill direction to each layer according to the specified template. "
"The template is a comma-separated list of angles in degrees, e.g. '0,90'. "
"The first angle is applied to the first layer, the second angle to the second layer, and so on. "
"If there are more layers than angles, the angles will be repeated. Note that not all sparse infill patterns support rotation.");
def->tooltip = L("Rotate the sparse infill direction per layer using a template of angles. "
"Enter comma-separated degrees (e.g., '0,30,60,90'). "
"Angles are applied in order by layer and repeat when the list ends. "
"Advanced syntax is supported: '+5' rotates +5° every layer; '+5#5' rotates +5° every 5 layers. See the Wiki for details. "
"When a template is set, the standard infill direction setting is ignored. "
"Note: some infill patterns (e.g., Gyroid) control rotation themselves; use with care.");
def->sidetext = L("°");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString("0,90"));
def->set_default_value(new ConfigOptionString(""));
//Orca
def = this->add("solid_infill_rotate_template", coString);
def->label = L("Solid infill rotatation template");
def->label = L("Solid infill rotation template");
def->category = L("Strength");
def->tooltip = L("This parameter adds a rotation of solid infill direction to each layer according to the specified template. "
"The template is a comma-separated list of angles in degrees, e.g. '0,90'. "
"The first angle is applied to the first layer, the second angle to the second layer, and so on. "
"If there are more layers than angles, the angles will be repeated. Note that not all solid infill patterns support rotation.");
def->sidetext = L("°");
def->sidetext = "°"; // degrees, don't need translation
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString("0,90"));
def->set_default_value(new ConfigOptionString(""));
def = this->add("skeleton_infill_density", coPercent);
def->label = L("Skeleton infill density");
def->category = L("Strength");
def->tooltip = L("The remaining part of the model contour after removing a certain depth from the surface is called the skeleton. This parameter is used to adjust the density of this section."
"When two regions have the same sparse infill settings but different skeleton densities, their skeleton areas will develop overlapping sections."
"default is as same as infill density.");
def->tooltip = L("The remaining part of the model contour after removing a certain depth from the surface is called the skeleton. "
"This parameter is used to adjust the density of this section. "
"When two regions have the same sparse infill settings but different skeleton densities, their skeleton areas will develop overlapping sections. "
"Default is as same as infill density.");
def->sidetext = "%";
def->min = 0;
def->max = 100;
@@ -3404,9 +3414,10 @@ void PrintConfigDef::init_fff_params()
def = this->add("skin_infill_density", coPercent);
def->label = L("Skin infill density");
def->category = L("Strength");
def->tooltip = L("The portion of the model's outer surface within a certain depth range is called the skin. This parameter is used to adjust the density of this section."
"When two regions have the same sparse infill settings but different skin densities, This area will not be split into two separate regions."
"default is as same as infill density.");
def->tooltip = L("The portion of the model's outer surface within a certain depth range is called the skin. "
"This parameter is used to adjust the density of this section. "
"When two regions have the same sparse infill settings but different skin densities, this area will not be split into two separate regions. "
"Default is as same as infill density.");
def->sidetext = "%";
def->min = 0;
def->max = 100;
@@ -3454,9 +3465,9 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloatOrPercent(100, true));
def = this->add("symmetric_infill_y_axis", coBool);
def->label = L("Symmetric infill y axis");
def->label = L("Symmetric infill Y axis");
def->category = L("Strength");
def->tooltip = L("If the model has two parts that are symmetric about the y-axis,"
def->tooltip = L("If the model has two parts that are symmetric about the Y axis,"
" and you want these parts to have symmetric textures, please click this option on one of the parts.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
@@ -3693,7 +3704,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("ironing_speed", coFloat);
def->label = L("Ironing speed");
def->category = L("Quality");
def->tooltip = L("Print speed of ironing lines");
def->tooltip = L("Print speed of ironing lines.");
def->sidetext = "mm/s"; // milimeters per second, don't need translation
def->min = 1;
def->mode = comAdvanced;
@@ -3858,7 +3869,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("machine_max_junction_deviation", coFloats);
def->full_label = L("Maximum Junction Deviation");
def->category = L("Machine limits");
def->tooltip = L("Maximum junction deviation (M205 J, only apply if JD > 0 for Marlin Firmware)");
def->tooltip = L("Maximum junction deviation (M205 J, only apply if JD > 0 for Marlin Firmware)");
def->sidetext = "mm"; // milimeters, don't need translation
def->min = 0;
def->mode = comAdvanced;
@@ -4044,7 +4055,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("nozzle_diameter", coFloats);
def->label = L("Nozzle diameter");
def->tooltip = L("Diameter of nozzle");
def->tooltip = L("The diameter of nozzle.");
def->sidetext = "mm"; // milimeters, don't need translation
def->mode = comAdvanced;
def->max = 100;
@@ -4218,7 +4229,7 @@ void PrintConfigDef::init_fff_params()
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
def->label = L("Walls");
def->category = L("Extruders");
def->tooltip = L("Filament to print walls");
def->tooltip = L("Filament to print walls.");
def->min = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(1));
@@ -4277,7 +4288,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("printer_model", coString);
def->label = L("Printer type");
def->tooltip = L("Type of the printer");
def->tooltip = L("Type of the printer.");
def->set_default_value(new ConfigOptionString());
def->cli = ConfigOptionDef::nocli;
@@ -4812,10 +4823,10 @@ void PrintConfigDef::init_fff_params()
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(80,true));
def = this->add("skirt_distance", coFloat);
def->label = L("Skirt distance");
def->tooltip = L("Distance from skirt to brim or object");
def->tooltip = L("The distance from the skirt to the brim or the object.");
def->sidetext = "mm"; // milimeters, don't need translation
def->min = 0;
def->max = 60;
@@ -4924,7 +4935,7 @@ void PrintConfigDef::init_fff_params()
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
def->label = L("Solid infill");
def->category = L("Extruders");
def->tooltip = L("Filament to print solid infill");
def->tooltip = L("Filament to print solid infill.");
def->min = 1;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(1));
@@ -5089,7 +5100,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("enable_filament_ramming", coBool);
def->label = L("Enable filament ramming");
def->tooltip = L("Enable filament ramming.");
def->tooltip = L("Enable filament ramming");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
@@ -5940,7 +5951,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("wipe_tower_rotation_angle", coFloat);
def->label = L("Wipe tower rotation angle");
def->tooltip = L("Wipe tower rotation angle with respect to x-axis.");
def->tooltip = L("Wipe tower rotation angle with respect to X axis.");
def->sidetext = "°"; // degrees, don't need translation
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
@@ -5994,7 +6005,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("wipe_tower_extra_rib_length", coFloat);
def->label = L("Extra rib length");
def->tooltip = L("Positive values can increase the size of the rib wall, while negative values can reduce the size."
def->tooltip = L("Positive values can increase the size of the rib wall, while negative values can reduce the size. "
"However, the size of the rib wall can not be smaller than that determined by the cleaning volume.");
def->sidetext = "mm"; // milimeters, don't need translation
def->max = 300;
@@ -6003,7 +6014,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("wipe_tower_rib_width", coFloat);
def->label = L("Rib width");
def->tooltip = L("Rib width");
def->tooltip = L("Rib width.");
def->sidetext = "mm"; // milimeters, don't need translation
def->mode = comAdvanced;
def->min = 0;
@@ -9345,7 +9356,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->set_default_value(new ConfigOptionString());
def = this->add("load_filament_ids", coInts);
def->label = L("Load filament ids");
def->label = L("Load filament IDs");
def->tooltip = L("Load filament IDs for each object.");
def->cli_params = "\"1,2,3,1\"";
def->set_default_value(new ConfigOptionInts());
@@ -9373,25 +9384,25 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("makerlab_name", coString);
def->label = L("MakerLab name");
def->tooltip = L("MakerLab name to generate this 3mf");
def->tooltip = L("MakerLab name to generate this 3mf.");
def->cli_params = "name";
def->set_default_value(new ConfigOptionString());
def = this->add("makerlab_version", coString);
def->label = L("MakerLab version");
def->tooltip = L("MakerLab version to generate this 3mf");
def->tooltip = L("MakerLab version to generate this 3mf.");
def->cli_params = "version";
def->set_default_value(new ConfigOptionString());
def = this->add("metadata_name", coStrings);
def->label = L("metadata name list");
def->tooltip = L("metadata name list added into 3mf");
def->tooltip = L("metadata name list added into 3mf.");
def->cli_params = "\"name1;name2;...\"";
def->set_default_value(new ConfigOptionStrings());
def = this->add("metadata_value", coStrings);
def->label = L("metadata value list");
def->tooltip = L("metadata value list added into 3mf");
def->tooltip = L("metadata value list added into 3mf.");
def->cli_params = "\"value1;value2;...\"";
def->set_default_value(new ConfigOptionStrings());
@@ -9741,11 +9752,11 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef()
def->tooltip = L("Index of the current layer. One-based (i.e. first layer is number 1).");
def = this->add("layer_z", coFloat);
def->label = L("Layer z");
def->label = L("Layer Z");
def->tooltip = L("Height of the current layer above the print bed, measured to the top of the layer.");
def = this->add("max_layer_z", coFloat);
def->label = L("Maximal layer z");
def->label = L("Maximal layer Z");
def->tooltip = L("Height of the last layer above the print bed.");
def = this->add("filament_extruder_id", coInt);
@@ -9755,32 +9766,32 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef()
// change_filament_gcode
new_def("previous_extruder", coInt, "Previous extruder", "Index of the extruder that is being unloaded. The index is zero based (first extruder has index 0).");
new_def("next_extruder", coInt, "Next extruder", "Index of the extruder that is being loaded. The index is zero based (first extruder has index 0).");
new_def("relative_e_axis", coBool, "Relative e-axis", "Indicates if relative positioning is being used");
new_def("toolchange_count", coInt, "Toolchange count", "The number of toolchanges throught the print");
new_def("relative_e_axis", coBool, "Relative e-axis", "Indicates if relative positioning is being used.");
new_def("toolchange_count", coInt, "Toolchange count", "The number of toolchanges throught the print.");
new_def("fan_speed", coNone, "", ""); //Option is no longer used and is zeroed by placeholder parser for compatability
new_def("old_retract_length", coFloat, "Old retract length", "The retraction length of the previous filament");
new_def("new_retract_length", coFloat, "New retract length", "The retraction lenght of the new filament");
new_def("old_retract_length_toolchange", coFloat, "Old retract length toolchange", "The toolchange retraction length of the previous filament");
new_def("new_retract_length_toolchange", coFloat, "New retract length toolchange", "The toolchange retraction length of the new filament");
new_def("old_filament_temp", coInt, "Old filament temp", "The old filament temp");
new_def("new_filament_temp", coInt, "New filament temp", "The new filament temp");
new_def("x_after_toolchange", coFloat, "X after toolchange", "The x pos after toolchange");
new_def("y_after_toolchange", coFloat, "Y after toolchange", "The y pos after toolchange");
new_def("z_after_toolchange", coFloat, "Z after toolchange", "The z pos after toolchange");
new_def("first_flush_volume", coFloat, "First flush volume", "The first flush volume");
new_def("second_flush_volume", coFloat, "Second flush volume", "The second flush volume");
new_def("old_filament_e_feedrate", coInt, "Old filament e feedrate", "The old filament extruder feedrate");
new_def("new_filament_e_feedrate", coInt, "New filament e feedrate", "The new filament extruder feedrate");
new_def("travel_point_1_x", coFloat, "Travel point 1 x", "The travel point 1 x");
new_def("travel_point_1_y", coFloat, "Travel point 1 y", "The travel point 1 y");
new_def("travel_point_2_x", coFloat, "Travel point 2 x", "The travel point 2 x");
new_def("travel_point_2_y", coFloat, "Travel point 2 y", "The travel point 2 y");
new_def("travel_point_3_x", coFloat, "Travel point 3 x", "The travel point 3 x");
new_def("travel_point_3_y", coFloat, "Travel point 3 y", "The travel point 3 y");
new_def("flush_length_1", coFloat, "Flush Length 1", "The first flush length");
new_def("flush_length_2", coFloat, "Flush Length 2", "The second flush length");
new_def("flush_length_3", coFloat, "Flush Length 3", "The third flush length");
new_def("flush_length_4", coFloat, "Flush Length 4", "The fourth flush length");
new_def("old_retract_length", coFloat, "Old retract length", "The retraction length of the previous filament.");
new_def("new_retract_length", coFloat, "New retract length", "The retraction lenght of the new filament.");
new_def("old_retract_length_toolchange", coFloat, "Old retract length toolchange", "The toolchange retraction length of the previous filament.");
new_def("new_retract_length_toolchange", coFloat, "New retract length toolchange", "The toolchange retraction length of the new filament.");
new_def("old_filament_temp", coInt, "Old filament temp", "The old filament temp.");
new_def("new_filament_temp", coInt, "New filament temp", "The new filament temp.");
new_def("x_after_toolchange", coFloat, "X after toolchange", "The X pos after toolchange.");
new_def("y_after_toolchange", coFloat, "Y after toolchange", "The Y pos after toolchange.");
new_def("z_after_toolchange", coFloat, "Z after toolchange", "The Z pos after toolchange.");
new_def("first_flush_volume", coFloat, "First flush volume", "The first flush volume.");
new_def("second_flush_volume", coFloat, "Second flush volume", "The second flush volume.");
new_def("old_filament_e_feedrate", coInt, "Old filament e feedrate", "The old filament extruder feedrate.");
new_def("new_filament_e_feedrate", coInt, "New filament e feedrate", "The new filament extruder feedrate.");
new_def("travel_point_1_x", coFloat, "Travel point 1 X", "The travel point 1 X.");
new_def("travel_point_1_y", coFloat, "Travel point 1 Y", "The travel point 1 Y.");
new_def("travel_point_2_x", coFloat, "Travel point 2 X", "The travel point 2 X.");
new_def("travel_point_2_y", coFloat, "Travel point 2 Y", "The travel point 2 Y.");
new_def("travel_point_3_x", coFloat, "Travel point 3 X", "The travel point 3 X.");
new_def("travel_point_3_y", coFloat, "Travel point 3 Y", "The travel point 3 Y.");
new_def("flush_length_1", coFloat, "Flush Length 1", "The first flush length.");
new_def("flush_length_2", coFloat, "Flush Length 2", "The second flush length.");
new_def("flush_length_3", coFloat, "Flush Length 3", "The third flush length.");
new_def("flush_length_4", coFloat, "Flush Length 4", "The fourth flush length.");
// change_extrusion_role_gcode
std::string extrusion_role_types = "Possible Values:\n[\"Perimeter\", \"ExternalPerimeter\", "

View File

@@ -1044,6 +1044,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, lateral_lattice_angle_2))
((ConfigOptionFloat, infill_overhang_angle))
((ConfigOptionBool, align_infill_direction_to_model))
((ConfigOptionString, extra_solid_infills))
((ConfigOptionEnum<FuzzySkinType>, fuzzy_skin))
((ConfigOptionFloat, fuzzy_skin_thickness))
((ConfigOptionFloat, fuzzy_skin_point_distance))

View File

@@ -1203,6 +1203,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "infill_direction"
|| opt_key == "solid_infill_direction"
|| opt_key == "align_infill_direction_to_model"
|| opt_key == "extra_solid_infills"
|| opt_key == "ensure_vertical_shell_thickness"
|| opt_key == "bridge_angle"
|| opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override
@@ -1268,7 +1269,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "overhang_reverse_internal_only"
|| opt_key == "overhang_reverse_threshold"
|| opt_key == "wall_direction"
//BBS
|| opt_key == "enable_overhang_speed"
|| opt_key == "detect_thin_wall"
|| opt_key == "precise_outer_wall") {
@@ -3613,16 +3613,14 @@ void PrintObject::discover_horizontal_shells()
Layer *layer = m_layers[i];
LayerRegion *layerm = layer->regions()[region_id];
const PrintRegionConfig &region_config = layerm->region().config();
#if 0
if (region_config.solid_infill_every_layers.value > 0 && region_config.sparse_infill_density.value > 0 &&
(i % region_config.solid_infill_every_layers) == 0) {
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge.
SurfaceType type = (region_config.sparse_infill_density == 100 || region_config.solid_infill_every_layers == 1) ? stInternalSolid : stInternalBridge;
for (Surface &surface : layerm->fill_surfaces.surfaces)
if (!region_config.extra_solid_infills.value.empty() &&
check_layer_id_pattern(region_config.extra_solid_infills.value, i)) {
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid.
for (Surface& surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal)
surface.surface_type = type;
surface.surface_type = stInternalSolid;
}
#endif
// If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
if (region_config.ensure_vertical_shell_thickness.value == evstAll)

View File

@@ -695,6 +695,8 @@ void copy_directory_recursively(const boost::filesystem::path &source, const boo
void save_string_file(const boost::filesystem::path& p, const std::string& str);
void load_string_file(const boost::filesystem::path& p, std::string& str);
bool check_layer_id_pattern(const std::string& pattern, int layer_id);
} // namespace Slic3r
#if WIN32

View File

@@ -1578,4 +1578,91 @@ void load_string_file(const boost::filesystem::path& p, std::string& str)
file.read(&str[0], sz);
}
// pattern string supprt these pattern: "
// 1. 5#1, insert 1 solid layer every 5 layers. this can be simplified to 5
// 2."1,7,9", explicitly insert solid layer at layer 1, 7, 9
bool check_layer_id_pattern(const std::string& pattern, int layer_id){
if (pattern.empty() || layer_id < 0)
return false;
// layer_id is 0-based, so we need to add 1 to make it 1-based
layer_id++;
// Remove whitespace and surrounding quotes.
std::string p; p.reserve(pattern.size());
for (char c : pattern) {
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
continue;
p.push_back(c);
}
if (!p.empty() && (p.front() == '"' || p.front() == '\''))
p.erase(p.begin());
if (!p.empty() && (p.back() == '"' || p.back() == '\''))
p.pop_back();
if (p.empty())
return false;
// Explicit list form: "1,7,9" or with counts per entry: "5,9#2,18"
if (p.find(',') != std::string::npos) {
size_t start = 0;
while (start < p.size()) {
size_t end = p.find(',', start);
std::string token = p.substr(start, (end == std::string::npos) ? std::string::npos : end - start);
if (!token.empty()) {
try {
size_t hash_pos_token = token.find('#');
if (hash_pos_token == std::string::npos) {
int value = std::stoi(token);
if (value == layer_id)
return true;
} else {
int base_layer = std::stoi(token.substr(0, hash_pos_token));
std::string count_str = token.substr(hash_pos_token + 1);
int local_count = 1;
if (!count_str.empty())
local_count = std::stoi(count_str);
if (base_layer > 0 && local_count > 0) {
if (layer_id >= base_layer && layer_id < base_layer + local_count)
return true;
}
}
} catch (...) {
// Ignore invalid tokens
}
}
if (end == std::string::npos)
break;
start = end + 1;
}
return false;
}
// Interval form: "N#K" or simplified "N" (equals to N#1)
int interval = 0;
int count = 1;
size_t hash_pos = p.find('#');
try {
if (hash_pos == std::string::npos) {
interval = std::stoi(p);
} else {
interval = std::stoi(p.substr(0, hash_pos));
std::string count_str = p.substr(hash_pos + 1);
if (!count_str.empty())
count = std::stoi(count_str);
}
} catch (...) {
return false;
}
if (interval <= 0 || count <= 0)
return false;
// Layers are 1-based. Match layers interval, interval+1, ..., interval+count-1, then repeat every interval.
if (layer_id < interval)
return false;
int mod = layer_id % interval; // For multiples, mod == 0
return mod >= 0 && mod < count;
}
}; // namespace Slic3r