diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 2b0f53a368..2e83d0829f 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -655,6 +655,7 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p params.lattice_angle_1 = region_config.lattice_angle_1; params.lattice_angle_2 = region_config.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); @@ -703,10 +704,15 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p } } 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)); + params.angle += float(Geometry::deg2rad(region_config.infill_direction.value)); } else { - params.angle = float(Geometry::deg2rad(region_config.solid_infill_direction.value)); + params.angle += float(Geometry::deg2rad(region_config.solid_infill_direction.value)); } // Calculate the actual flow we'll be using for this infill. @@ -720,9 +726,9 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p if (!params.bridge) { if (params.extrusion_role == erInternalInfill) params.sparse_infill_speed = region_config.sparse_infill_speed; - else if (params.extrusion_role == erTopSolidInfill) + else if (params.extrusion_role == erTopSolidInfill) { params.top_surface_speed = region_config.top_surface_speed; - else if (params.extrusion_role == erSolidInfill) + } else if (params.extrusion_role == erSolidInfill) params.solid_infill_speed = region_config.internal_solid_infill_speed; } // Calculate flow spacing for infill pattern generation. @@ -992,7 +998,6 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: 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(); - f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; if (surface_fill.params.pattern == ipConcentricInternal) { FillConcentricInternal *fill_concentric = dynamic_cast(f.get()); assert(fill_concentric != nullptr); @@ -1045,14 +1050,186 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: params.using_internal_flow = using_internal_flow; params.no_extrusion_overlap = surface_fill.params.overlap; auto ®ion_config = layerm->region().config(); + params.config = ®ion_config; + params.pattern = surface_fill.params.pattern; ConfigOptionFloats rotate_angles; - rotate_angles.deserialize( surface_fill.params.extrusion_role == erInternalInfill ? region_config.sparse_infill_rotate_template.value : region_config.solid_infill_rotate_template.value); - auto rotate_angle_idx = f->layer_id % rotate_angles.size(); - f->rotate_angle = Geometry::deg2rad(rotate_angles.values[rotate_angle_idx]); + 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 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 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::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]); + } - params.config = ®ion_config; - params.pattern = surface_fill.params.pattern; if( surface_fill.params.pattern == ipLockedZag ) { params.locked_zag = true; params.infill_lock_depth = surface_fill.params.infill_lock_depth; diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 5cc4c23150..bc95d25012 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -787,6 +787,7 @@ static std::vector 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", "lattice_angle_1", "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", "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", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 7dcb83dd66..24e29459ed 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2366,6 +2366,14 @@ 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->category = L("Strength"); + def->tooltip = L("Aligns infill and surface fill directions to follow the model's orientation on the build plate. When enabled, fill directions rotate with the model to maintain optimal strength characteristics."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + // Infill multiline def = this->add("fill_multiline", coInt); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index bea60fc749..c2f38ae0b0 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -983,6 +983,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, lattice_angle_1)) ((ConfigOptionFloat, lattice_angle_2)) ((ConfigOptionFloat, infill_overhang_angle)) + ((ConfigOptionBool, align_infill_direction_to_model)) ((ConfigOptionEnum, fuzzy_skin)) ((ConfigOptionFloat, fuzzy_skin_thickness)) ((ConfigOptionFloat, fuzzy_skin_point_distance)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c84a265610..fbc56dceed 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1078,6 +1078,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "skeleton_infill_line_width" || opt_key == "infill_direction" || opt_key == "solid_infill_direction" + || opt_key == "align_infill_direction_to_model" || opt_key == "ensure_vertical_shell_thickness" || opt_key == "bridge_angle" || opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index d7c9408103..094d3e7be6 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -431,10 +431,34 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true set_value(str, true); } } else if (m_opt.opt_key == "sparse_infill_rotate_template" || m_opt.opt_key == "solid_infill_rotate_template") { - if (!ConfigOptionFloats::validate_string(str.utf8_string())) { - show_error(m_parent, format_wxstr(_L("This parameter expects a comma-delimited list of numbers. E.g, \"0,90\"."))); - wxString old_value(boost::any_cast(m_value)); - this->set_value(old_value, true); // Revert to previous value + string ustr(str.utf8_string()); + if (!ConfigOptionFloats::validate_string(ustr)) { + string v; + std::smatch match; + string ps = (m_opt.opt_key == "sparse_infill_rotate_template") ? + u8"[SODMR]?[BT][!]?|[SODMR]?[#][\\d]+[!]?|[+\\-]?[\\d.]+[%]?[*]?[\\d]*[SODMR]?[/NnZz$LlUuQq~^|#]?[+\\-]?[\\d.]*[%#\'\"cm]?[m]?[BT]?[!*]?" : + u8"[#][\\d]+[!]?|[+\\-]?[\\d.]+[%]?[*]?[\\d]*[/NnZz$LlUuQq~^|#]?[+\\-]?[\\d.]*[%#\'\"cm]?[m]?[!*]?"; + + //if (m_opt.opt_key == "sparse_infill_rotate_template") { + //string ps = u8"[#][\\d]+[!]?|[+\\-]?[\\d.]+[%]?[*]?[\\d]*[SODMR]?[/NnZz$LlUuQq~^|#]?[+\\-]?[\\d.]*[%#\'\"cm]?[m]?["; + //if (m_opt.opt_key == "sparse_infill_rotate_template") { + // ps = u8"[BT][!]?|" + ps ; + //} + //ps += u8"BT]?[!*]?"; + while (std::regex_search(ustr, match, std::regex(ps))) { + for (auto x : match) v += x.str() + ", "; + ustr = match.suffix().str(); + } + v = v.substr(0, v.length() - 2); + try { + this->set_value(from_u8(v), true); + m_value = into_u8(v); + } catch (...) { + show_error(m_parent, format_wxstr(_L("This parameter expects a valid template."))); + wxString old_value(boost::any_cast(m_value)); + this->set_value(old_value, true); // Revert to previous value + throw; + } } else { // Valid string, so update m_value with the new string from the control. m_value = into_u8(str); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index e23f57991c..5acd8cadbc 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -107,6 +107,7 @@ std::map> SettingsFactory::PART_CAT { L("Strength"), {{"wall_loops", "",1},{"top_shell_layers", L("Top Solid Layers"),1},{"top_shell_thickness", L("Top Minimum Shell Thickness"),1},{"top_surface_density", L("Top Surface Density"),1}, {"bottom_shell_layers", L("Bottom Solid Layers"),1}, {"bottom_shell_thickness", L("Bottom Minimum Shell Thickness"),1},{"bottom_surface_density", L("Bottom Surface Density"),1}, {"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"lattice_angle_1", "",1},{"lattice_angle_2", "",1},{"infill_overhang_angle", "",1},{"infill_anchor", "",1},{"infill_anchor_max", "",1},{"top_surface_pattern", "",1},{"bottom_surface_pattern", "",1}, {"internal_solid_infill_pattern", "",1}, + {"align_infill_direction_to_model", "", 1}, {"infill_combination", "",1}, {"infill_combination_max_layer_height", "",1}, {"infill_wall_overlap", "",1},{"top_bottom_infill_wall_overlap", "",1}, {"solid_infill_direction", "",1}, {"infill_direction", "",1}, {"bridge_angle", "",1}, {"internal_bridge_angle", "",1}, {"minimum_sparse_infill_area", "",1} }}, { L("Speed"), {{"outer_wall_speed", "",1},{"inner_wall_speed", "",2},{"sparse_infill_speed", "",3},{"top_surface_speed", "",4}, {"internal_solid_infill_speed", "",5}, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index eba57e60d6..ef069ab337 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -9902,6 +9902,7 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i _obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f)); _obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45)); _obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135)); + _obj->config.set_key_value("align_infill_direction_to_model", new ConfigOptionBool(true)); _obj->config.set_key_value("ironing_type", new ConfigOptionEnum(IroningType::NoIroning)); _obj->config.set_key_value("internal_solid_infill_speed", new ConfigOptionFloat(internal_solid_speed)); _obj->config.set_key_value("top_surface_speed", new ConfigOptionFloat(top_surface_speed)); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a6532c6850..c909258d4b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2195,6 +2195,7 @@ void TabPrint::build() optgroup->append_single_option_line("detect_thin_wall", "strength_settings_walls#detect-thin-wall"); optgroup = page->new_optgroup(L("Top/bottom shells"), L"param_shell"); + optgroup->append_single_option_line("top_shell_layers", "strength_settings_top_bottom_shells#shells-layers"); optgroup->append_single_option_line("top_shell_thickness", "strength_settings_top_bottom_shells#shell-thickness"); optgroup->append_single_option_line("top_surface_density", "strength_settings_top_bottom_shells#surface-density"); @@ -2233,6 +2234,7 @@ void TabPrint::build() optgroup->append_single_option_line("infill_wall_overlap", "strength_settings_infill#infill-wall-overlap"); optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); + optgroup->append_single_option_line("align_infill_direction_to_model"); optgroup->append_single_option_line("bridge_angle", "strength_settings_advanced#bridge-infill-direction"); optgroup->append_single_option_line("internal_bridge_angle", "strength_settings_advanced#bridge-infill-direction"); // ORCA: Internal bridge angle override optgroup->append_single_option_line("minimum_sparse_infill_area", "strength_settings_advanced#minimum-sparse-infill-threshold");