mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-22 12:45:17 +00:00
[Feature] Add a new feature that allow user to insert extra solid infills (#10611)
* refactor Infill rotation template * clean up comments * set default solid_infill_rotate_template to empty * Fix an issue that infill_direction solid_infill_direction not working as expected * Add Extra Solid Infill Feature Introduced a new feature to insert extra solid infills at specific layers for enhanced strength in 3D prints. * fix doc error * fix image name * support "#K" for Explicit Layer List * update wiki
This commit is contained in:
BIN
doc/images/fill/extra-solid-infill.gif
Normal file
BIN
doc/images/fill/extra-solid-infill.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
@@ -13,6 +13,7 @@ Infill is the internal structure of a 3D print, providing strength and support.
|
||||
- [Filter out tiny gaps](#filter-out-tiny-gaps)
|
||||
- [Anchor](#anchor)
|
||||
- [Internal Solid Infill](#internal-solid-infill)
|
||||
- [Extra Solid Infill](#extra-solid-infill)
|
||||
- [Sparse Infill Pattern](#sparse-infill-pattern)
|
||||
- [Credits](#credits)
|
||||
|
||||
@@ -153,11 +154,56 @@ OrcaSlicer tries to connect two close infill lines to a short perimeter segment.
|
||||
- **Anchor On**
|
||||
|
||||

|
||||
|
||||
## Internal Solid Infill
|
||||
|
||||
Line pattern of internal solid infill. If the [detect narrow internal solid infill](strength_settings_advanced#detect-narrow-internal-solid-infill) be enabled, the [concentric pattern](strength_settings_patterns#concentric) will be used for the small area.
|
||||
|
||||
|
||||
## Extra Solid Infill
|
||||
|
||||
Insert extra solid infills at specific layers to add strength at critical points in your print. This feature allows you to strategically reinforce your part without changing the overall sparse infill density.
|
||||
|
||||

|
||||
|
||||
The pattern supports two formats:
|
||||
|
||||
### Interval Pattern
|
||||
- **Simple interval**: `N` - Insert 1 solid layer every N layers, equal to `N#1`
|
||||
- **Multiple layers**: `N#K` - Insert K consecutive solid layers every N layers
|
||||
- **Optional K**: `N#` - Shorthand for `N#1`
|
||||
|
||||
Examples:
|
||||
```
|
||||
5 or 5#1 # Insert 1 solid layer every 5 layers
|
||||
5# # Same as 5#1
|
||||
10#2 # Insert 2 consecutive solid layers every 10 layers
|
||||
```
|
||||
|
||||
### Explicit Layer List
|
||||
Specify exact layer numbers (1-based) using comma-separated values. Each entry may be a single layer `N` or a range `N#K` to insert K consecutive solid layers starting at layer N:
|
||||
|
||||
```
|
||||
1,7,9 # Insert solid layers at layers 1, 7, and 9
|
||||
5,15,25 # Insert solid layers at layers 5, 15, and 25
|
||||
5,9#2,18 # Insert at 5; at 9 and 10 (because #2); and at 18
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> - Layer numbers are 1-based (first layer is layer 1)
|
||||
> - `#K` is optional in both interval and explicit list entries (`N#` equals `N#1`)
|
||||
> - Solid layers are inserted in addition to the normal sparse infill pattern
|
||||
|
||||
> [!TIP]
|
||||
> Use this feature to:
|
||||
> - Add strength at stress concentration points
|
||||
> - Reinforce mounting holes or attachment points
|
||||
> - Create internal structure for functional parts
|
||||
> - Add periodic reinforcement for tall prints
|
||||
> - Insert a single solid layer at a specific height by using an explicit list with a leading 0, which will be ignored because layer indices are 1-based. Example: `0,15` inserts a solid layer only at layer 15.
|
||||
|
||||
> [!WARNING]
|
||||
> Layers that include solid infill can take significantly longer than surrounding layers. This time differential may lead to z-banding-like bulges. Consider adjusting cooling or speeds if you observe artifacts.
|
||||
|
||||
## Sparse Infill Pattern
|
||||
|
||||
> [!TIP]
|
||||
|
||||
@@ -794,7 +794,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",
|
||||
|
||||
@@ -2384,6 +2384,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);
|
||||
@@ -3184,8 +3191,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("sparse_infill_rotate_template", coString);
|
||||
def->label = L("Sparse infill rotatation template");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L(
|
||||
"Rotate the sparse infill direction per layer using a template of angles. "
|
||||
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. "
|
||||
|
||||
@@ -985,6 +985,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))
|
||||
|
||||
@@ -1079,6 +1079,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
|
||||
@@ -3489,16 +3490,14 @@ void PrintObject::discover_horizontal_shells()
|
||||
Layer *layer = m_layers[i];
|
||||
LayerRegion *layerm = layer->regions()[region_id];
|
||||
const PrintRegionConfig ®ion_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)
|
||||
|
||||
@@ -681,6 +681,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
|
||||
|
||||
@@ -1566,4 +1566,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
|
||||
|
||||
@@ -451,13 +451,27 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
||||
show_error(m_parent, format_wxstr(_L("This parameter expects a valid template.")));
|
||||
wxString old_value(boost::any_cast<std::string>(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);
|
||||
}
|
||||
break;
|
||||
} else if (m_opt.opt_key == "extra_solid_infills") {
|
||||
string ustr(str.utf8_string());
|
||||
// New rule: accept either interval form (N or N#K) or explicit list (e.g. 1,7,9), with optional quotes.
|
||||
const std::regex rx_interval(u8R"(^\s*['"]?\s*\d+\s*(?:#\s*\d*)?\s*['"]?\s*$)");
|
||||
// List entries may be plain numbers or number with optional #K count, e.g., 5, 9#2, 18
|
||||
const std::regex rx_list(u8R"(^\s*['"]?\s*\d+(?:\s*#\s*\d*)?(?:\s*,\s*\d+(?:\s*#\s*\d*)?)*\s*['"]?\s*$)");
|
||||
bool is_valid = ustr.empty() || std::regex_match(ustr, rx_interval) || std::regex_match(ustr, rx_list);
|
||||
if (!is_valid) {
|
||||
show_error(m_parent, format_wxstr(_L("Invalid pattern. Use N, N#K, or a comma-separated list with optional #K per entry. Examples: 5, 5#2, 1,7,9, 5,9#2,18.")));
|
||||
wxString old_value(boost::any_cast<std::string>(m_value));
|
||||
this->set_value(old_value, true); // Revert to previous value
|
||||
}
|
||||
// Valid string or empty, so update m_value with the new string from the control.
|
||||
m_value = into_u8(str);
|
||||
break;
|
||||
}
|
||||
|
||||
m_value = into_u8(str);
|
||||
|
||||
@@ -108,6 +108,7 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::PART_CAT
|
||||
{"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},{"lateral_lattice_angle_1", "",1},{"lateral_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},
|
||||
{"extra_solid_infills", "", 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},
|
||||
|
||||
@@ -2263,6 +2263,7 @@ void TabPrint::build()
|
||||
|
||||
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
|
||||
optgroup->append_single_option_line("align_infill_direction_to_model", "strength_settings_advanced#align-infill-direction-to-model");
|
||||
optgroup->append_single_option_line("extra_solid_infills", "strength_settings_infill#extra-solid-infill");
|
||||
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");
|
||||
|
||||
Reference in New Issue
Block a user