Add explicit infill filament override setting with per-layer control

Introduce enable_infill_filament_override toggle to replace the implicit
behavior where sparse_infill_filament != wall_filament implied an override.
Add infill_filament_use_base_first_layers and infill_filament_use_base_last_layers
to keep the base filament on boundary layers before switching to the infill
override filament. Move the sparse infill filament selector into the infill
section of the UI and gate its visibility behind the new toggle.

Also fixes: filament deletion now correctly updates wall/infill/solid filament
assignments, LayerRegion::flow() resolves nozzle diameter from the effective
extruder per role, and mixed filament preview ratio calculation is improved
for edge cases (0% and 100%).
This commit is contained in:
Rad
2026-03-24 15:35:11 +01:00
parent 778a8e45af
commit d4947f3cbe
20 changed files with 440 additions and 139 deletions
+2 -1
View File
@@ -41,4 +41,5 @@ resources/profiles/user/default
deps_src/build/
.claude/
.omc
nul
nul
.codex_tmp
+1 -1
View File
@@ -1082,7 +1082,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
}
if (internal_solid_fill == nullptr) {
// Produce another solid fill.
params.extruder = layerm.region().extruder(frSolidInfill);
params.extruder = layerm.extruder(frSolidInfill);
const auto top_pattern = layerm.region().config().top_surface_pattern;
if(top_pattern == ipMonotonic || top_pattern == ipMonotonicLine)
params.pattern = top_pattern;
+32 -14
View File
@@ -4767,15 +4767,32 @@ LayerResult GCode::process_layer(const Print& print,
size_t pointillism_path_split_segments = 0;
size_t pointillism_path_split_fallbacks = 0;
auto configured_filament_id_1based = [&layer_tools](const ExtrusionEntityCollection& entities, const PrintRegion& region) -> unsigned int {
if (layer_tools.extruder_override != 0)
return layer_tools.extruder_override;
if (entities.has_infill()) {
if (entities.has_solid_infill())
return region.config().solid_infill_filament.value;
return region.config().sparse_infill_filament.value;
auto configured_filament_id_1based = [&layer_tools](const GCode::ObjectByExtruder::Island::Region::Type entity_type,
const ExtrusionEntityCollection& entities,
const PrintRegion& region) -> unsigned int {
if (entity_type == GCode::ObjectByExtruder::Island::Region::INFILL) {
const ExtrusionRole role = entities.entities.empty() ? erNone : entities.entities.front()->role();
if (role == erSolidInfill && std::abs(region.config().sparse_infill_density.value - 100.) < EPSILON)
return layer_tools.sparse_infill_filament(region) + 1;
if (is_solid_infill(role))
return layer_tools.solid_infill_filament(region) + 1;
return layer_tools.sparse_infill_filament(region) + 1;
}
return region.config().wall_filament.value;
return layer_tools.wall_filament(region) + 1;
};
auto configured_extruder_id = [&layer_tools](const GCode::ObjectByExtruder::Island::Region::Type entity_type,
const ExtrusionEntityCollection& entities,
const PrintRegion& region) -> int {
if (entity_type == GCode::ObjectByExtruder::Island::Region::INFILL) {
const ExtrusionRole role = entities.entities.empty() ? erNone : entities.entities.front()->role();
if (role == erSolidInfill && std::abs(region.config().sparse_infill_density.value - 100.) < EPSILON)
return int(layer_tools.sparse_infill_filament(region));
if (is_solid_infill(role))
return int(layer_tools.solid_infill_filament(region));
return int(layer_tools.sparse_infill_filament(region));
}
return int(layer_tools.wall_filament(region));
};
auto pointillism_sequence_for_filament = [&](unsigned int filament_id_1based) -> const std::vector<unsigned int>* {
@@ -4798,8 +4815,9 @@ LayerResult GCode::process_layer(const Print& print,
return inserted.first->second.empty() ? nullptr : &inserted.first->second;
};
auto grouped_manual_pattern_mixed_filament_id =
[&layer_tools, &configured_filament_id_1based](const ExtrusionEntityCollection& entities,
const PrintRegion& region) -> unsigned int {
[&layer_tools, &configured_filament_id_1based](const GCode::ObjectByExtruder::Island::Region::Type entity_type,
const ExtrusionEntityCollection& entities,
const PrintRegion& region) -> unsigned int {
if (layer_tools.mixed_mgr == nullptr || layer_tools.num_physical == 0)
return 0;
@@ -4813,7 +4831,7 @@ LayerResult GCode::process_layer(const Print& print,
return normalized_pattern.find(',') != std::string::npos;
};
const unsigned int configured_filament_id = configured_filament_id_1based(entities, region);
const unsigned int configured_filament_id = configured_filament_id_1based(entity_type, entities, region);
if (has_grouped_pattern(configured_filament_id))
return configured_filament_id;
return 0;
@@ -5239,7 +5257,7 @@ LayerResult GCode::process_layer(const Print& print,
local_z_clipped_collections.emplace_back(std::move(clipped_base));
}
const unsigned int configured_filament_id = configured_filament_id_1based(*filtered_extrusions, region);
const unsigned int configured_filament_id = configured_filament_id_1based(entity_type, *filtered_extrusions, region);
const std::vector<unsigned int>* pointillism_sequence =
is_anything_overridden ? nullptr : pointillism_sequence_for_filament(configured_filament_id);
if (pointillism_sequence != nullptr) {
@@ -5283,14 +5301,14 @@ LayerResult GCode::process_layer(const Print& print,
}
// This extrusion is part of certain Region, which tells us which extruder should be used for it:
int correct_extruder_id = layer_tools.extruder(*filtered_extrusions, region);
int correct_extruder_id = configured_extruder_id(entity_type, *filtered_extrusions, region);
if (!is_anything_overridden &&
entity_type == ObjectByExtruder::Island::Region::PERIMETERS &&
layer_tools.mixed_mgr != nullptr &&
layer_tools.num_physical > 0 &&
correct_extruder_id >= 0) {
const unsigned int mixed_filament_id =
grouped_manual_pattern_mixed_filament_id(*filtered_extrusions, region);
grouped_manual_pattern_mixed_filament_id(entity_type, *filtered_extrusions, region);
if (mixed_filament_id != 0) {
std::vector<std::unique_ptr<ExtrusionEntityCollection>> split_by_extruder;
size_t bucket_count = 0;
+47 -19
View File
@@ -79,6 +79,36 @@ void append_unique_preserve_order(std::vector<unsigned int> &dst, unsigned int v
dst.emplace_back(value);
}
bool internal_solid_infill_uses_sparse_filament(const PrintRegion &region, ExtrusionRole role)
{
return role == erSolidInfill && std::abs(region.config().sparse_infill_density.value - 100.) < EPSILON;
}
bool use_base_infill_filament(const LayerTools &layer_tools, const PrintRegion &region)
{
const PrintRegionConfig &config = region.config();
if (!config.enable_infill_filament_override.value)
return true;
if (layer_tools.object_layer_count <= 0)
return false;
const int first_layers = std::max(0, config.infill_filament_use_base_first_layers.value);
const int last_layers = std::max(0, config.infill_filament_use_base_last_layers.value);
return layer_tools.layer_index < first_layers || layer_tools.layer_index >= layer_tools.object_layer_count - last_layers;
}
unsigned int sparse_infill_filament_id_1based(const LayerTools &layer_tools, const PrintRegion &region)
{
return use_base_infill_filament(layer_tools, region) ? region.config().wall_filament.value : region.config().sparse_infill_filament.value;
}
unsigned int infill_filament_id_1based(const LayerTools &layer_tools, const PrintRegion &region, ExtrusionRole role)
{
if (internal_solid_infill_uses_sparse_filament(region, role))
return sparse_infill_filament_id_1based(layer_tools, region);
return is_solid_infill(role) ? region.config().solid_infill_filament.value : sparse_infill_filament_id_1based(layer_tools, region);
}
unsigned int grouped_manual_pattern_mixed_filament_id_for_layer(const LayerTools& layer_tools,
unsigned int configured_filament_id_1based)
{
@@ -247,8 +277,8 @@ unsigned int LayerTools::wall_filament(const PrintRegion &region) const
unsigned int LayerTools::sparse_infill_filament(const PrintRegion &region) const
{
assert(region.config().sparse_infill_filament.value > 0);
unsigned int id = (this->extruder_override == 0) ? region.config().sparse_infill_filament.value : this->extruder_override;
assert(region.config().wall_filament.value > 0);
unsigned int id = (this->extruder_override == 0) ? sparse_infill_filament_id_1based(*this, region) : this->extruder_override;
return resolve_mixed_1based(id) - 1;
}
@@ -269,10 +299,8 @@ unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, c
unsigned int extruder = 1;
if (this->extruder_override == 0) {
if (extrusions.has_infill()) {
if (extrusions.has_solid_infill())
extruder = region.config().solid_infill_filament;
else
extruder = region.config().sparse_infill_filament;
const ExtrusionRole role = extrusions.entities.empty() ? erNone : extrusions.entities.front()->role();
extruder = infill_filament_id_1based(*this, region, role);
} else
extruder = region.config().wall_filament.value;
} else
@@ -653,8 +681,9 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// Store the sequential layer index and mixed-filament context for resolution.
layer_tools.layer_index = layerCount;
layer_tools.layer_height = layer->height;
layer_tools.layer_index = layerCount;
layer_tools.object_layer_count = int(object.layers().size());
layer_tools.layer_height = layer->height;
// Override extruder with the next
for (; it_per_layer_extruder_override != per_layer_extruder_switches.end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override)
@@ -712,17 +741,19 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
layer_tools.has_object = true;
}
bool has_infill = false;
bool has_solid_infill = false;
bool has_sparse_infill = false;
bool has_solid_infill = false;
bool something_nonoverriddable = false;
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
ExtrusionRole role = fill->entities.empty() ? erNone : fill->entities.front()->role();
if (is_solid_infill(role))
if (internal_solid_infill_uses_sparse_filament(region, role))
has_sparse_infill = true;
else if (is_solid_infill(role))
has_solid_infill = true;
else if (role != erNone)
has_infill = true;
has_sparse_infill = true;
if (m_print_config_ptr) {
if (! layer_tools.wiping_extrusions().is_overriddable_and_mark(*fill, *m_print_config_ptr, object, region))
@@ -737,18 +768,15 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
layerCount,
float(layer->print_z),
float(layer->height)));
if (has_infill)
layer_tools.extruders.emplace_back(resolve_mixed(region.config().sparse_infill_filament,
layerCount,
float(layer->print_z),
float(layer->height)));
} else if (has_solid_infill || has_infill)
if (has_sparse_infill)
layer_tools.extruders.emplace_back(layer_tools.sparse_infill_filament(region) + 1);
} else if (has_solid_infill || has_sparse_infill)
layer_tools.extruders.emplace_back(resolve_mixed(extruder_override,
layerCount,
float(layer->print_z),
float(layer->height)));
}
if (has_solid_infill || has_infill)
if (has_solid_infill || has_sparse_infill)
layer_tools.has_object = true;
}
layerCount++;
+2
View File
@@ -123,6 +123,8 @@ public:
unsigned int extruder_override = 0;
// Sequential layer index (0-based), used by mixed-filament resolution.
int layer_index = 0;
// Total number of object layers for the current print object.
int object_layer_count = 0;
// Actual layer height for this print_z where available.
coordf_t layer_height = 0.;
// Should a skirt be printed at this layer?
+1
View File
@@ -71,6 +71,7 @@ public:
// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection fills;
unsigned int extruder(FlowRole role) const;
Flow flow(FlowRole role) const;
Flow flow(FlowRole role, double layer_height) const;
Flow bridging_flow(FlowRole role, bool thick_bridge = false) const;
+53 -2
View File
@@ -1,6 +1,7 @@
#include "Layer.hpp"
#include "BridgeDetector.hpp"
#include "ClipperUtils.hpp"
#include "Exception.hpp"
#include "Geometry.hpp"
#include "PerimeterGenerator.hpp"
#include "Print.hpp"
@@ -9,6 +10,8 @@
#include "SVG.hpp"
#include "Algorithm/RegionExpansion.hpp"
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
@@ -17,6 +20,32 @@
namespace Slic3r {
namespace {
bool use_base_infill_filament(const PrintRegionConfig &config, int layer_index, int layer_count)
{
if (!config.enable_infill_filament_override.value)
return true;
if (layer_count <= 0)
return false;
const int first_layers = std::max(0, config.infill_filament_use_base_first_layers.value);
const int last_layers = std::max(0, config.infill_filament_use_base_last_layers.value);
return layer_index < first_layers || layer_index >= layer_count - last_layers;
}
} // namespace
unsigned int LayerRegion::extruder(FlowRole role) const
{
const PrintRegionConfig &config = this->region().config();
if (role == frInfill)
return use_base_infill_filament(config, m_layer->id(), int(m_layer->object()->layers().size())) ? config.wall_filament.value : config.sparse_infill_filament.value;
if (role == frSolidInfill && std::abs(config.sparse_infill_density.value - 100.) < EPSILON)
return use_base_infill_filament(config, m_layer->id(), int(m_layer->object()->layers().size())) ? config.wall_filament.value : config.sparse_infill_filament.value;
return this->region().extruder(role);
}
Flow LayerRegion::flow(FlowRole role) const
{
return this->flow(role, m_layer->height);
@@ -24,7 +53,29 @@ Flow LayerRegion::flow(FlowRole role) const
Flow LayerRegion::flow(FlowRole role, double layer_height) const
{
return m_region->flow(*m_layer->object(), role, layer_height, m_layer->id() == 0);
const PrintConfig &print_config = m_layer->object()->print()->config();
ConfigOptionFloatOrPercent config_width;
if (m_layer->id() == 0 && print_config.initial_layer_line_width.value > 0) {
config_width = print_config.initial_layer_line_width;
} else if (role == frExternalPerimeter) {
config_width = m_region->config().outer_wall_line_width;
} else if (role == frPerimeter) {
config_width = m_region->config().inner_wall_line_width;
} else if (role == frInfill) {
config_width = m_region->config().sparse_infill_line_width;
} else if (role == frSolidInfill) {
config_width = m_region->config().internal_solid_infill_line_width;
} else if (role == frTopSolidInfill) {
config_width = m_region->config().top_surface_line_width;
} else {
throw Slic3r::InvalidArgument("Unknown role");
}
if (config_width.value == 0)
config_width = m_layer->object()->config().line_width;
const auto nozzle_diameter = float(print_config.nozzle_diameter.get_at(this->extruder(role) - 1));
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height));
}
Flow LayerRegion::bridging_flow(FlowRole role, bool thick_bridge) const
@@ -33,7 +84,7 @@ Flow LayerRegion::bridging_flow(FlowRole role, bool thick_bridge) const
const PrintRegionConfig &region_config = region.config();
const PrintObject &print_object = *this->layer()->object();
Flow bridge_flow;
auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(this->extruder(role) - 1));
if (thick_bridge) {
// The old Slic3r way (different from all other slicers): Use rounded extrusions.
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
+1 -1
View File
@@ -856,7 +856,7 @@ static std::vector<std::string> s_Preset_print_options {
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges","enable_extra_bridge_layer", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
"filename_format", "wall_filament", "support_bottom_z_distance",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
"enable_infill_filament_override", "infill_filament_use_base_first_layers", "infill_filament_use_base_last_layers", "sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
"ooze_prevention", "standby_temperature_delta", "preheat_time","delta_temperature","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width", "inner_wall_line_width",
"outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
"skin_infill_line_width","skeleton_infill_line_width",
+39
View File
@@ -3321,6 +3321,31 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(100., true));
def = this->add("enable_infill_filament_override", coBool);
def->label = L("Override infill filament");
def->category = L("Extruders");
def->tooltip = L("Allow this print, object, or part to use a dedicated filament for sparse infill instead of inheriting its regular filament.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("infill_filament_use_base_first_layers", coInt);
def->label = L("Base infill on first layers");
def->category = L("Extruders");
def->tooltip = L("Keep using the regular object filament for this many bottom infill layers before switching to the infill override filament.");
def->sidetext = L("layers");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(0));
def = this->add("infill_filament_use_base_last_layers", coInt);
def->label = L("Base infill on last layers");
def->category = L("Extruders");
def->tooltip = L("Keep using the regular object filament for this many top infill layers after switching back from the infill override filament.");
def->sidetext = L("layers");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(0));
def = this->add("sparse_infill_filament", coInt);
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
def->label = L("Infill");
@@ -7279,6 +7304,13 @@ void DynamicPrintConfig::normalize_fdm(int used_filaments)
}
}
if (!this->has("enable_infill_filament_override") && this->has("sparse_infill_filament")) {
const int wall_filament = this->has("wall_filament") ? this->option("wall_filament")->getInt() : 1;
const int sparse_infill_filament = this->option("sparse_infill_filament")->getInt();
if (sparse_infill_filament > 0 && sparse_infill_filament != wall_filament)
this->opt<ConfigOptionBool>("enable_infill_filament_override", true)->value = true;
}
if (this->has("wipe_tower_filament")) {
// If invalid, replace with 0.
int extruder = this->opt<ConfigOptionInt>("wipe_tower_filament")->value;
@@ -7360,6 +7392,13 @@ void DynamicPrintConfig::normalize_fdm_1()
}
}
if (!this->has("enable_infill_filament_override") && this->has("sparse_infill_filament")) {
const int wall_filament = this->has("wall_filament") ? this->option("wall_filament")->getInt() : 1;
const int sparse_infill_filament = this->option("sparse_infill_filament")->getInt();
if (sparse_infill_filament > 0 && sparse_infill_filament != wall_filament)
this->opt<ConfigOptionBool>("enable_infill_filament_override", true)->value = true;
}
if (!this->has("solid_infill_filament") && this->has("sparse_infill_filament"))
this->option("solid_infill_filament", true)->setInt(this->option("sparse_infill_filament")->getInt());
+3
View File
@@ -1003,6 +1003,9 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInt, fuzzy_skin_octaves))
((ConfigOptionFloat, fuzzy_skin_persistence))
((ConfigOptionFloat, gap_infill_speed))
((ConfigOptionBool, enable_infill_filament_override))
((ConfigOptionInt, infill_filament_use_base_first_layers))
((ConfigOptionInt, infill_filament_use_base_last_layers))
((ConfigOptionInt, sparse_infill_filament))
((ConfigOptionFloatOrPercent, sparse_infill_line_width))
((ConfigOptionPercent, infill_wall_overlap))
+27 -4
View File
@@ -1086,6 +1086,9 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "bottom_shell_thickness"
|| opt_key == "top_shell_thickness"
|| opt_key == "minimum_sparse_infill_area"
|| opt_key == "enable_infill_filament_override"
|| opt_key == "infill_filament_use_base_first_layers"
|| opt_key == "infill_filament_use_base_last_layers"
|| opt_key == "sparse_infill_filament"
|| opt_key == "solid_infill_filament"
|| opt_key == "sparse_infill_line_width"
@@ -3198,6 +3201,7 @@ PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObject
}
const std::string key_extruder { "extruder" };
const std::string key_enable_infill_filament_override { "enable_infill_filament_override" };
static constexpr const std::initializer_list<const std::string_view> keys_extruders { "sparse_infill_filament"sv, "solid_infill_filament"sv, "wall_filament"sv };
static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPrintConfig &in)
@@ -3211,10 +3215,23 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr
out.solid_infill_filament.value = extruder;
out.wall_filament.value = extruder;
}
const auto *opt_enable_infill_filament_override = in.opt<ConfigOptionBool>(key_enable_infill_filament_override);
const auto *opt_sparse_infill_filament = in.opt<ConfigOptionInt>("sparse_infill_filament");
const bool allow_local_infill_filament_override = opt_enable_infill_filament_override != nullptr ?
opt_enable_infill_filament_override->value :
(opt_sparse_infill_filament != nullptr &&
opt_sparse_infill_filament->value > 0 &&
opt_sparse_infill_filament->value != out.wall_filament.value);
if (opt_enable_infill_filament_override != nullptr)
out.enable_infill_filament_override.value = opt_enable_infill_filament_override->value;
else if (opt_sparse_infill_filament != nullptr)
out.enable_infill_filament_override.value = allow_local_infill_filament_override;
// 2) Copy the rest of the values.
for (auto it = in.cbegin(); it != in.cend(); ++ it)
if (it->first != key_extruder)
if (it->first != key_extruder && it->first != key_enable_infill_filament_override)
if (ConfigOption* my_opt = out.option(it->first, false); my_opt != nullptr) {
if (it->first == "sparse_infill_filament" && !allow_local_infill_filament_override)
continue;
if (one_of(it->first, keys_extruders)) {
// Ignore "default" extruders.
int extruder = static_cast<const ConfigOptionInt*>(it->second.get())->value;
@@ -3296,6 +3313,8 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full
print_config.apply(full_config, true);
object_config.apply(full_config, true);
default_region_config.apply(full_config, true);
default_region_config.enable_infill_filament_override.value = false;
default_region_config.sparse_infill_filament.value = default_region_config.wall_filament.value;
// BBS
size_t filament_extruders = print_config.filament_diameter.size();
object_config = object_config_from_model_object(object_config, model_object, filament_extruders);
@@ -3310,6 +3329,9 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full
object_extruders);
for (const std::pair<const t_layer_height_range, ModelConfig> &range_and_config : model_object.layer_config_ranges)
if (range_and_config.second.has("wall_filament") ||
range_and_config.second.has("enable_infill_filament_override") ||
range_and_config.second.has("infill_filament_use_base_first_layers") ||
range_and_config.second.has("infill_filament_use_base_last_layers") ||
range_and_config.second.has("sparse_infill_filament") ||
range_and_config.second.has("solid_infill_filament"))
PrintRegion::collect_object_printing_extruders(
@@ -4278,9 +4300,10 @@ void PrintObject::combine_infill()
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
//FIXME limit the layer height to max_layer_height
double nozzle_diameter = std::min(
this->print()->config().nozzle_diameter.get_at(region.config().sparse_infill_filament.value - 1),
this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_filament.value - 1));
double nozzle_diameter = this->print()->config().nozzle_diameter.get_at(region.config().wall_filament.value - 1);
if (region.config().enable_infill_filament_override.value)
nozzle_diameter = std::min(nozzle_diameter, this->print()->config().nozzle_diameter.get_at(region.config().sparse_infill_filament.value - 1));
nozzle_diameter = std::min(nozzle_diameter, this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_filament.value - 1));
//Orca: Limit combination of infill to up to infill_combination_max_layer_height
const double infill_combination_max_layer_height = region.config().infill_combination_max_layer_height.get_abs_value(nozzle_diameter);
+28 -4
View File
@@ -1,8 +1,24 @@
#include "Exception.hpp"
#include "Print.hpp"
#include <cmath>
namespace Slic3r {
namespace {
unsigned int effective_sparse_infill_filament(const PrintRegionConfig &config)
{
return config.enable_infill_filament_override.value ? config.sparse_infill_filament.value : config.wall_filament.value;
}
bool internal_solid_infill_uses_sparse_filament(const PrintRegionConfig &config, FlowRole role)
{
return role == frSolidInfill && std::abs(config.sparse_infill_density.value - 100.) < EPSILON;
}
} // namespace
// 1-based extruder identifier for this region and role.
unsigned int PrintRegion::extruder(FlowRole role) const
{
@@ -10,8 +26,10 @@ unsigned int PrintRegion::extruder(FlowRole role) const
if (role == frPerimeter || role == frExternalPerimeter)
extruder = m_config.wall_filament;
else if (role == frInfill)
extruder = m_config.sparse_infill_filament;
else if (role == frSolidInfill || role == frTopSolidInfill)
extruder = effective_sparse_infill_filament(m_config);
else if (role == frSolidInfill)
extruder = internal_solid_infill_uses_sparse_filament(m_config, role) ? effective_sparse_infill_filament(m_config) : m_config.solid_infill_filament;
else if (role == frTopSolidInfill)
extruder = m_config.solid_infill_filament;
else
throw Slic3r::InvalidArgument("Unknown role");
@@ -52,7 +70,7 @@ Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_he
coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
{
return (print_config.nozzle_diameter.get_at(m_config.wall_filament.value - 1) +
print_config.nozzle_diameter.get_at(m_config.sparse_infill_filament.value - 1) +
print_config.nozzle_diameter.get_at(effective_sparse_infill_filament(m_config) - 1) +
print_config.nozzle_diameter.get_at(m_config.solid_infill_filament.value - 1)) / 3.;
}
@@ -70,10 +88,16 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con
int i = std::max(0, extruder_id - 1);
object_extruders.emplace_back((i >= num_extruders) ? 0 : i);
};
const bool use_base_infill_boundary_layers =
region_config.enable_infill_filament_override.value &&
region_config.sparse_infill_density.value > 0 &&
(region_config.infill_filament_use_base_first_layers.value > 0 || region_config.infill_filament_use_base_last_layers.value > 0);
if (region_config.wall_loops.value > 0 || has_brim)
emplace_extruder(region_config.wall_filament);
if (use_base_infill_boundary_layers)
emplace_extruder(region_config.wall_filament);
if (region_config.sparse_infill_density.value > 0)
emplace_extruder(region_config.sparse_infill_filament);
emplace_extruder(int(effective_sparse_infill_filament(region_config)));
if (region_config.top_shell_layers.value > 0 || region_config.bottom_shell_layers.value > 0)
emplace_extruder(region_config.solid_infill_filament);
}
+10 -3
View File
@@ -536,6 +536,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
auto gcflavor = preset_bundle->printers.get_edited_preset().config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value;
const bool bSEMM = preset_bundle->printers.get_edited_preset().config.opt_bool("single_extruder_multi_material");
bool have_volumetric_extrusion_rate_slope = config->option<ConfigOptionFloat>("max_volumetric_extrusion_rate_slope")->value > 0;
float have_volumetric_extrusion_rate_slope_segment_length = config->option<ConfigOptionFloat>("max_volumetric_extrusion_rate_slope_segment_length")->value;
@@ -556,10 +557,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_field(el, have_perimeters);
bool have_infill = config->option<ConfigOptionPercent>("sparse_infill_density")->value > 0;
const bool show_infill_filament_override_toggle = have_infill && !bSEMM;
// sparse_infill_filament uses the same logic as in Print::extruders()
for (auto el : { "sparse_infill_pattern", "infill_combination",
"minimum_sparse_infill_area", "sparse_infill_filament", "infill_anchor_max","infill_shift_step","sparse_infill_rotate_template","symmetric_infill_y_axis"})
"minimum_sparse_infill_area", "infill_anchor_max","infill_shift_step","sparse_infill_rotate_template","symmetric_infill_y_axis"})
toggle_line(el, have_infill);
toggle_line("enable_infill_filament_override", show_infill_filament_override_toggle);
bool have_combined_infill = config->opt_bool("infill_combination") && have_infill;
toggle_line("infill_combination_max_layer_height", have_combined_infill);
@@ -748,8 +751,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
toggle_field("single_extruder_multi_material", !is_BBL_Printer);
auto bSEMM = preset_bundle->printers.get_edited_preset().config.opt_bool("single_extruder_multi_material");
toggle_field("ooze_prevention", !bSEMM);
bool have_ooze_prevention = config->opt_bool("ooze_prevention");
toggle_line("standby_temperature_delta", have_ooze_prevention);
@@ -764,6 +765,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
for (auto el : {"wall_filament", "sparse_infill_filament", "solid_infill_filament", "wipe_tower_filament"})
toggle_line(el, !bSEMM);
const bool show_sparse_infill_filament =
show_infill_filament_override_toggle && config->opt_bool("enable_infill_filament_override");
toggle_line("infill_filament_use_base_first_layers", show_sparse_infill_filament);
toggle_line("infill_filament_use_base_last_layers", show_sparse_infill_filament);
toggle_line("sparse_infill_filament", show_sparse_infill_filament);
bool purge_in_primetower = preset_bundle->printers.get_edited_preset().config.opt_bool("purge_in_prime_tower");
for (auto el : {"wipe_tower_rotation_angle", "wipe_tower_cone_angle",
+14 -5
View File
@@ -9390,8 +9390,9 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
if (is_selected_separate_extruder)
{
const PrintRegionConfig& cfg = layerm->region().config();
if (cfg.wall_filament.value != m_selected_extruder ||
cfg.sparse_infill_filament.value != m_selected_extruder ||
const int effective_sparse_infill_filament = int(layerm->extruder(frInfill));
if (cfg.wall_filament.value != m_selected_extruder &&
effective_sparse_infill_filament != m_selected_extruder &&
cfg.solid_infill_filament.value != m_selected_extruder)
continue;
}
@@ -9403,10 +9404,18 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (! fill->entities.empty())
{
const int effective_sparse_infill_filament = int(layerm->extruder(frInfill));
_3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
select_geometry(idx_layer, is_solid_infill(fill->entities.front()->role()) ?
layerm->region().config().solid_infill_filament :
layerm->region().config().sparse_infill_filament, 1));
select_geometry(idx_layer,
(fill->entities.front()->role() == erSolidInfill &&
std::abs(layerm->region().config().sparse_infill_density.value - 100.) < EPSILON) ?
int(layerm->extruder(frSolidInfill)) :
(is_solid_infill(fill->entities.front()->role()) ?
layerm->region().config().solid_infill_filament :
effective_sparse_infill_filament),
1));
}
}
}
}
+1 -1
View File
@@ -145,7 +145,7 @@ std::map<std::string, std::vector<SimpleSettingData>> 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},{"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},
{"sparse_infill_density", "",1},{"sparse_infill_pattern", "",1},{"enable_infill_filament_override", "",1},{"infill_filament_use_base_first_layers", "",1},{"infill_filament_use_base_last_layers", "",1},{"sparse_infill_filament", "",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}
+5 -3
View File
@@ -701,7 +701,8 @@ void ObjectList::update_filament_values_for_items(const size_t filaments_count)
}
m_objects_model->SetExtruder(extruder, item);
static const char *keys[] = {"support_filament", "support_interface_filament"};
static const char *keys[] = {"wall_filament", "sparse_infill_filament", "solid_infill_filament",
"support_filament", "support_interface_filament"};
for (auto key : keys)
if (object->config.has(key) && object->config.opt_int(key) > filaments_count)
object->config.erase(key);
@@ -876,7 +877,8 @@ void ObjectList::update_filament_values_for_items_when_delete_filament(const siz
}
m_objects_model->SetExtruder(extruder, item);
static const char* keys[] = {"support_filament", "support_interface_filament"};
static const char* keys[] = {"wall_filament", "sparse_infill_filament", "solid_infill_filament",
"support_filament", "support_interface_filament"};
for (auto key : keys) {
if (object->config.has(key)) {
if (object->config.opt_int(key) == filament_id + 1)
@@ -903,7 +905,7 @@ void ObjectList::update_filament_values_for_items_when_delete_filament(const siz
int new_value = object->volumes[id]->config.opt_int(key) > filament_id ?
object->volumes[id]->config.opt_int(key) - 1 :
object->volumes[id]->config.opt_int(key);
object->config.set_key_value(key, new ConfigOptionInt(new_value));
object->volumes[id]->config.set_key_value(key, new ConfigOptionInt(new_value));
}
}
}
+50 -24
View File
@@ -75,6 +75,44 @@ namespace GUI {
class Bed3D;
namespace {
struct ResolvedInfillFilament
{
int wall_filament = 1;
int sparse_infill_filament = 1;
bool override_enabled = false;
};
template <typename ConfigLike>
ResolvedInfillFilament resolve_infill_filament(const ConfigLike &config, int inherited_wall_filament, bool inherited_override_enabled, int inherited_sparse_infill_filament)
{
ResolvedInfillFilament resolved;
resolved.wall_filament = inherited_wall_filament;
resolved.sparse_infill_filament = inherited_sparse_infill_filament;
resolved.override_enabled = inherited_override_enabled;
if (const ConfigOption *wall_opt = config.option("wall_filament"); wall_opt != nullptr)
resolved.wall_filament = wall_opt->getInt();
const ConfigOption *sparse_opt = config.option("sparse_infill_filament");
const ConfigOption *override_opt = config.option("enable_infill_filament_override");
if (override_opt != nullptr)
resolved.override_enabled = override_opt->getBool();
else if (sparse_opt != nullptr)
resolved.override_enabled = sparse_opt->getInt() != resolved.wall_filament;
if (sparse_opt != nullptr)
resolved.sparse_infill_filament = sparse_opt->getInt();
if (!resolved.override_enabled)
resolved.sparse_infill_filament = resolved.wall_filament;
return resolved;
}
} // namespace
ColorRGBA PartPlate::SELECT_COLOR = { 0.2666f, 0.2784f, 0.2784f, 1.0f }; //{ 0.4196f, 0.4235f, 0.4235f, 1.0f };
ColorRGBA PartPlate::UNSELECT_COLOR = { 0.82f, 0.82f, 0.82f, 1.0f };
ColorRGBA PartPlate::UNSELECT_DARK_COLOR = { 0.384f, 0.384f, 0.412f, 1.0f };
@@ -1350,7 +1388,8 @@ std::vector<int> PartPlate::get_extruders(bool conside_custom_gcode) const
int glb_support_intf_extr = glb_config.opt_int("support_interface_filament");
int glb_support_extr = glb_config.opt_int("support_filament");
int glb_wall_extr = glb_config.opt_int("wall_filament");
int glb_sparse_infill_extr = glb_config.opt_int("sparse_infill_filament");
const ResolvedInfillFilament global_infill { glb_wall_extr, glb_wall_extr, false };
int glb_sparse_infill_extr = glb_wall_extr;
int glb_solid_infill_extr = glb_config.opt_int("solid_infill_filament");
bool glb_support = glb_config.opt_bool("enable_support");
glb_support |= glb_config.opt_int("raft_layers") > 0;
@@ -1405,23 +1444,16 @@ std::vector<int> PartPlate::get_extruders(bool conside_custom_gcode) const
plate_extruders.push_back(glb_support_extr);
}
int obj_wall_extr = 1;
int obj_wall_extr = glb_wall_extr;
const ConfigOption* wall_opt = mo->config.option("wall_filament");
if (wall_opt != nullptr)
obj_wall_extr = wall_opt->getInt();
if (obj_wall_extr != 1)
plate_extruders.push_back(obj_wall_extr);
else if (glb_wall_extr != 1)
plate_extruders.push_back(glb_wall_extr);
int obj_sparse_infill_extr = 1;
const ConfigOption* sparse_infill_opt = mo->config.option("sparse_infill_filament");
if (sparse_infill_opt != nullptr)
obj_sparse_infill_extr = sparse_infill_opt->getInt();
if (obj_sparse_infill_extr != 1)
plate_extruders.push_back(obj_sparse_infill_extr);
else if (glb_sparse_infill_extr != 1)
plate_extruders.push_back(glb_sparse_infill_extr);
const ResolvedInfillFilament object_infill = resolve_infill_filament(mo->config, glb_wall_extr, global_infill.override_enabled, glb_sparse_infill_extr);
if (object_infill.override_enabled && object_infill.sparse_infill_filament != 1)
plate_extruders.push_back(object_infill.sparse_infill_filament);
int obj_solid_infill_extr = 1;
const ConfigOption* solid_infill_opt = mo->config.option("solid_infill_filament");
@@ -1463,7 +1495,8 @@ std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D
int glb_support_intf_extr = full_config.opt_int("support_interface_filament");
int glb_support_extr = full_config.opt_int("support_filament");
int glb_wall_extr = full_config.opt_int("wall_filament");
int glb_sparse_infill_extr = full_config.opt_int("sparse_infill_filament");
const ResolvedInfillFilament global_infill { glb_wall_extr, glb_wall_extr, false };
int glb_sparse_infill_extr = glb_wall_extr;
int glb_solid_infill_extr = full_config.opt_int("solid_infill_filament");
bool glb_support = full_config.opt_bool("enable_support");
@@ -1528,23 +1561,16 @@ std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D
else if (glb_support_extr != 0)
plate_extruders.push_back(glb_support_extr);
int obj_wall_extr = 1;
int obj_wall_extr = glb_wall_extr;
const ConfigOption* wall_opt = object->config.option("wall_filament");
if (wall_opt != nullptr)
obj_wall_extr = wall_opt->getInt();
if (obj_wall_extr != 1)
plate_extruders.push_back(obj_wall_extr);
else if (glb_wall_extr != 1)
plate_extruders.push_back(glb_wall_extr);
int obj_sparse_infill_extr = 1;
const ConfigOption* sparse_infill_opt = object->config.option("sparse_infill_filament");
if (sparse_infill_opt != nullptr)
obj_sparse_infill_extr = sparse_infill_opt->getInt();
if (obj_sparse_infill_extr != 1)
plate_extruders.push_back(obj_sparse_infill_extr);
else if (glb_sparse_infill_extr != 1)
plate_extruders.push_back(glb_sparse_infill_extr);
const ResolvedInfillFilament object_infill = resolve_infill_filament(object->config, glb_wall_extr, global_infill.override_enabled, glb_sparse_infill_extr);
if (object_infill.override_enabled && object_infill.sparse_infill_filament != 1)
plate_extruders.push_back(object_infill.sparse_infill_filament);
int obj_solid_infill_extr = 1;
const ConfigOption* solid_infill_opt = object->config.option("solid_infill_filament");
+88 -49
View File
@@ -4638,7 +4638,7 @@ private:
static std::vector<int> decode_gradient_weights(const std::string &s, size_t n);
static std::vector<int> normalize_gradient_weights(const std::vector<int> &w, size_t n);
static std::string encode_gradient_weights(const std::vector<int> &w);
static std::vector<unsigned int> build_weighted_pair_sequence(unsigned int a, unsigned int b, int percent_b);
static std::vector<unsigned int> build_weighted_pair_sequence(unsigned int a, unsigned int b, int percent_b, bool limit_cycle = false);
static std::vector<unsigned int> build_weighted_multi_sequence(const std::vector<unsigned int> &ids,
const std::vector<int> &weights,
size_t max_cycle_limit = 0);
@@ -4891,31 +4891,90 @@ std::string MixedFilamentConfigPanel::encode_gradient_weights(const std::vector<
return out.str();
}
std::vector<unsigned int> MixedFilamentConfigPanel::build_weighted_pair_sequence(unsigned int a, unsigned int b, int percent_b)
namespace {
std::pair<int, int> effective_pair_preview_ratios(int percent_b)
{
std::vector<unsigned int> seq;
const int b_percent = std::clamp(percent_b, 0, 100);
int ratio_a = std::max(1, 100 - b_percent);
int ratio_b = std::max(1, b_percent);
const int g = std::gcd(ratio_a, ratio_b);
if (g > 1) {
ratio_a /= g;
ratio_b /= g;
const int mix_b = std::clamp(percent_b, 0, 100);
int ratio_a = 1;
int ratio_b = 0;
if (mix_b >= 100) {
ratio_a = 0;
ratio_b = 1;
} else if (mix_b > 0) {
const int pct_b = mix_b;
const int pct_a = 100 - pct_b;
const bool b_is_major = pct_b >= pct_a;
const int major_pct = b_is_major ? pct_b : pct_a;
const int minor_pct = b_is_major ? pct_a : pct_b;
const int major_layers =
std::max(1, int(std::lround(double(major_pct) / double(std::max(1, minor_pct)))));
ratio_a = b_is_major ? 1 : major_layers;
ratio_b = b_is_major ? major_layers : 1;
}
if (ratio_a > 0 && ratio_b > 0) {
const int g = std::gcd(ratio_a, ratio_b);
if (g > 1) {
ratio_a /= g;
ratio_b /= g;
}
}
return { std::max(0, ratio_a), std::max(0, ratio_b) };
}
std::vector<unsigned int> build_effective_pair_preview_sequence(unsigned int component_a,
unsigned int component_b,
int percent_b,
bool limit_cycle)
{
std::vector<unsigned int> sequence;
if (component_a == 0 || component_b == 0 || component_a == component_b)
return sequence;
auto [ratio_a, ratio_b] = effective_pair_preview_ratios(percent_b);
constexpr int k_max_cycle = 24;
if (ratio_a + ratio_b > k_max_cycle) {
if (limit_cycle && ratio_a > 0 && ratio_b > 0 && ratio_a + ratio_b > k_max_cycle) {
const double scale = double(k_max_cycle) / double(ratio_a + ratio_b);
ratio_a = std::max(1, int(std::round(double(ratio_a) * scale)));
ratio_b = std::max(1, int(std::round(double(ratio_b) * scale)));
}
if (ratio_a == 0 && ratio_b == 0)
ratio_a = 1;
const int cycle = std::max(1, ratio_a + ratio_b);
seq.reserve(size_t(cycle));
sequence.reserve(size_t(cycle));
for (int pos = 0; pos < cycle; ++pos) {
const int b_before = (pos * ratio_b) / cycle;
const int b_after = ((pos + 1) * ratio_b) / cycle;
seq.emplace_back((b_after > b_before) ? b : a);
sequence.emplace_back((b_after > b_before) ? component_b : component_a);
}
return seq;
return sequence;
}
std::string format_preview_sequence_percent(int count, int total)
{
if (count <= 0 || total <= 0)
return "";
const double percent = 100.0 * double(count) / double(total);
const double rounded_tenths = std::round(percent * 10.0) / 10.0;
const double nearest_integer = std::round(rounded_tenths);
if (std::abs(rounded_tenths - nearest_integer) < 1e-6)
return wxString::Format("%d%%", int(nearest_integer)).ToStdString();
return wxString::Format("%.1f%%", rounded_tenths).ToStdString();
}
} // namespace
std::vector<unsigned int> MixedFilamentConfigPanel::build_weighted_pair_sequence(unsigned int a,
unsigned int b,
int percent_b,
bool limit_cycle)
{
return build_effective_pair_preview_sequence(a, b, percent_b, limit_cycle);
}
static void reduce_weight_counts_to_cycle_limit(std::vector<int> &counts, size_t cycle_limit)
@@ -5412,7 +5471,7 @@ std::string MixedFilamentConfigPanel::summarize_sequence(const std::vector<unsig
std::string out;
for (auto &p : sorted) {
if (!out.empty()) out += "/";
out += wxString::Format("%d%%", int(100 * p.first / int(seq.size()))).ToStdString();
out += format_preview_sequence_percent(p.first, int(seq.size()));
}
return out;
}
@@ -5945,14 +6004,14 @@ void MixedFilamentConfigPanel::build_ui()
m_mf.gradient_component_ids.clear();
m_mf.gradient_component_weights.clear();
preview_mix_b_percent = effective_local_z_preview_mix_b_percent(m_mf, m_preview_settings);
preview_sequence = build_weighted_pair_sequence(m_mf.component_a, m_mf.component_b, preview_mix_b_percent);
preview_sequence = build_weighted_pair_sequence(m_mf.component_a, m_mf.component_b, preview_mix_b_percent, same_layer_mode);
}
}
m_mf.custom = true;
const std::vector<unsigned int> selected_gradient_ids = decode_gradient_ids(m_mf.gradient_component_ids);
if (preview_sequence.empty())
preview_sequence = build_weighted_pair_sequence(m_mf.component_a, m_mf.component_b, preview_mix_b_percent);
preview_sequence = build_weighted_pair_sequence(m_mf.component_a, m_mf.component_b, preview_mix_b_percent, same_layer_mode);
if (m_blend_selector && selected_gradient_ids.size() >= 3) {
std::vector<wxColour> corner_colors;
@@ -6203,7 +6262,8 @@ void MixedFilamentConfigPanel::update_preview()
else
initial_sequence = build_weighted_pair_sequence(m_mf.component_a,
m_mf.component_b,
effective_local_z_preview_mix_b_percent(m_mf, m_preview_settings));
effective_local_z_preview_mix_b_percent(m_mf, m_preview_settings),
same_layer_mode);
if (m_blend_selector && initial_gradient_ids.size() >= 3) {
std::vector<wxColour> corner_colors;
@@ -6666,31 +6726,6 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
}
return sequence;
};
auto build_weighted_pair_sequence = [](unsigned int component_a, unsigned int component_b, int mix_b_percent) {
std::vector<unsigned int> sequence;
const int b_percent = std::clamp(mix_b_percent, 0, 100);
int ratio_a = std::max(1, 100 - b_percent);
int ratio_b = std::max(1, b_percent);
const int g = std::gcd(ratio_a, ratio_b);
if (g > 1) {
ratio_a /= g;
ratio_b /= g;
}
constexpr int k_max_cycle = 24;
if (ratio_a + ratio_b > k_max_cycle) {
const double scale = double(k_max_cycle) / double(ratio_a + ratio_b);
ratio_a = std::max(1, int(std::round(double(ratio_a) * scale)));
ratio_b = std::max(1, int(std::round(double(ratio_b) * scale)));
}
const int cycle = std::max(1, ratio_a + ratio_b);
sequence.reserve(size_t(cycle));
for (int pos = 0; pos < cycle; ++pos) {
const int b_before = (pos * ratio_b) / cycle;
const int b_after = ((pos + 1) * ratio_b) / cycle;
sequence.emplace_back((b_after > b_before) ? component_b : component_a);
}
return sequence;
};
const bool height_weighted_mode = get_mixed_mode(false);
int gradient_mode = height_weighted_mode ? 1 : 0;
float lower_bound = std::max(0.01f, get_mixed_float("mixed_filament_height_lower_bound", 0.04f));
@@ -6776,8 +6811,7 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
return blended;
};
auto build_entry_preview_sequence = [decode_manual_pattern_ids, decode_gradient_ids, decode_gradient_weights,
build_weighted_multi_sequence,
build_weighted_pair_sequence, preview_settings](const MixedFilament &entry) {
build_weighted_multi_sequence, preview_settings](const MixedFilament &entry) {
const std::string normalized_pattern = MixedFilamentManager::normalize_manual_pattern(entry.manual_pattern);
if (!normalized_pattern.empty())
return decode_manual_pattern_ids(normalized_pattern, entry.component_a, entry.component_b);
@@ -6793,7 +6827,8 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
}
const int effective_mix_b = MixedFilamentConfigPanel::effective_local_z_preview_mix_b_percent(entry, preview_settings);
return build_weighted_pair_sequence(entry.component_a, entry.component_b, effective_mix_b);
const bool same_layer_mode = entry.distribution_mode == int(MixedFilament::SameLayerPointillisme);
return build_effective_pair_preview_sequence(entry.component_a, entry.component_b, effective_mix_b, same_layer_mode);
};
auto compute_entry_display_color = [num_physical, &physical_colors, blend_from_sequence, build_entry_preview_sequence](const MixedFilament &entry) {
const std::vector<unsigned int> sequence = build_entry_preview_sequence(entry);
@@ -8908,7 +8943,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
"extruder_colour", "filament_colour", "material_colour", "printable_height", "printer_model", "printer_technology",
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
"layer_height", "initial_layer_print_height", "min_layer_height", "max_layer_height",
"brim_width", "wall_loops", "wall_filament", "sparse_infill_density", "sparse_infill_filament", "top_shell_layers",
"brim_width", "wall_loops", "wall_filament", "sparse_infill_density", "enable_infill_filament_override", "infill_filament_use_base_first_layers", "infill_filament_use_base_last_layers", "sparse_infill_filament", "top_shell_layers",
"enable_support", "support_filament", "support_interface_filament",
"support_top_z_distance", "support_bottom_z_distance", "raft_layers",
"wipe_tower_rotation_angle", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extra_flow", "local_z_wipe_tower_purge_lines", "wipe_tower_max_purge_speed",
@@ -19708,8 +19743,9 @@ void Plater::on_filaments_delete(size_t num_filaments, size_t filament_id, int r
// update UI
sidebar().on_filaments_delete(filament_id);
// update global support filament
static const char* keys[] = {"support_filament", "support_interface_filament"};
// update global feature filament selections
static const char* keys[] = {"wall_filament", "sparse_infill_filament", "solid_infill_filament",
"support_filament", "support_interface_filament"};
for (auto key : keys)
if (p->config->has(key)) {
if (p->config->opt_int(key) == filament_id + 1)
@@ -19986,6 +20022,9 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
}
// Orca: update when *_filament changed
else if (opt_key == "support_interface_filament" || opt_key == "support_filament" || opt_key == "wall_filament" ||
opt_key == "enable_infill_filament_override" ||
opt_key == "infill_filament_use_base_first_layers" ||
opt_key == "infill_filament_use_base_last_layers" ||
opt_key == "sparse_infill_filament" || opt_key == "solid_infill_filament") {
update_scheduled = true;
}
+1 -1
View File
@@ -129,7 +129,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
return i <= 0 || i > num_extruders || idx_extruder == -1 || idx_extruder == i - 1;
};
bool perimeter_extruder_active = feature_extruder_active(print_config.opt_int("wall_filament"));
bool infill_extruder_active = feature_extruder_active(print_config.opt_int("sparse_infill_filament"));
bool infill_extruder_active = feature_extruder_active(print_config.opt_int("wall_filament"));
bool solid_infill_extruder_active = feature_extruder_active(print_config.opt_int("solid_infill_filament"));
bool support_material_extruder_active = feature_extruder_active(print_config.opt_int("support_filament"));
bool support_material_interface_extruder_active = feature_extruder_active(print_config.opt_int("support_interface_filament"));
+35 -7
View File
@@ -2267,6 +2267,10 @@ void TabPrint::build()
optgroup->append_single_option_line("sparse_infill_density", "strength_settings_infill#sparse-infill-density");
optgroup->append_single_option_line("fill_multiline", "strength_settings_infill#fill-multiline");
optgroup->append_single_option_line("sparse_infill_pattern", "strength_settings_infill#sparse-infill-pattern");
optgroup->append_single_option_line("enable_infill_filament_override");
optgroup->append_single_option_line("infill_filament_use_base_first_layers");
optgroup->append_single_option_line("infill_filament_use_base_last_layers");
optgroup->append_single_option_line("sparse_infill_filament", "multimaterial_settings_filament_for_features#infill");
optgroup->append_single_option_line("infill_direction", "strength_settings_infill#direction");
optgroup->append_single_option_line("sparse_infill_rotate_template", "strength_settings_infill_rotation_template_metalanguage");
optgroup->append_single_option_line("skin_infill_density", "strength_settings_patterns#locked-zag");
@@ -2457,7 +2461,6 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Filament for Features"), L"param_filament_for_features");
optgroup->append_single_option_line("wall_filament", "multimaterial_settings_filament_for_features#walls");
optgroup->append_single_option_line("sparse_infill_filament", "multimaterial_settings_filament_for_features#infill");
optgroup->append_single_option_line("solid_infill_filament", "multimaterial_settings_filament_for_features#solid-infill");
optgroup->append_single_option_line("wipe_tower_filament", "multimaterial_settings_filament_for_features#wipe-tower");
@@ -2710,6 +2713,31 @@ static std::vector<std::string> substruct(std::vector<std::string> const& l, std
return t;
}
static DynamicPrintConfig resolved_model_config_for_tab(const DynamicPrintConfig& config)
{
DynamicPrintConfig resolved(config);
const auto* infill_override_opt = config.option<ConfigOptionBool>("enable_infill_filament_override");
const bool infill_override_enabled = infill_override_opt != nullptr && infill_override_opt->value;
if (!infill_override_enabled && resolved.has("sparse_infill_filament"))
resolved.erase("sparse_infill_filament");
if (const auto* extruder_opt = config.option<ConfigOptionInt>("extruder"); extruder_opt != nullptr && extruder_opt->value > 0) {
const int extruder = extruder_opt->value;
if (!resolved.has("wall_filament"))
resolved.set_key_value("wall_filament", new ConfigOptionInt(extruder));
if (!resolved.has("sparse_infill_filament"))
resolved.set_key_value("sparse_infill_filament", new ConfigOptionInt(extruder));
if (!resolved.has("solid_infill_filament"))
resolved.set_key_value("solid_infill_filament", new ConfigOptionInt(extruder));
}
if (!resolved.has("solid_infill_filament") && resolved.has("sparse_infill_filament"))
resolved.set_key_value("solid_infill_filament", new ConfigOptionInt(resolved.opt_int("sparse_infill_filament")));
return resolved;
}
TabPrintModel::TabPrintModel(ParamsPanel* parent, std::vector<std::string> const & keys)
: TabPrint(parent, Preset::TYPE_MODEL)
, m_keys(intersect(Preset::print_options(), keys))
@@ -2778,19 +2806,19 @@ void TabPrintModel::update_model_config()
m_null_keys.clear();
if (!m_object_configs.empty()) {
DynamicPrintConfig const & global_config= *m_config;
DynamicPrintConfig const & local_config = m_object_configs.begin()->second->get();
const DynamicPrintConfig local_config = resolved_model_config_for_tab(m_object_configs.begin()->second->get());
DynamicPrintConfig diff_config;
std::vector<std::string> all_keys = local_config.keys(); // at least one has these keys
std::vector<std::string> local_keys = intersect(m_keys, all_keys); // all equal on these keys
if (m_object_configs.size() > 1) {
std::vector<std::string> global_keys = m_keys; // all equal with global on these keys
for (auto & config : m_object_configs) {
auto equals = global_config.equal(config.second->get());
const DynamicPrintConfig resolved_config = resolved_model_config_for_tab(config.second->get());
auto equals = global_config.equal(resolved_config);
global_keys = intersect(global_keys, equals);
diff_config.apply_only(config.second->get(), substruct(config.second->keys(), equals));
if (&config.second->get() == &local_config) continue;
all_keys = concat(all_keys, config.second->keys());
local_keys = intersect(local_keys, local_config.equal(config.second->get()));
diff_config.apply_only(resolved_config, substruct(resolved_config.keys(), equals));
all_keys = concat(all_keys, resolved_config.keys());
local_keys = intersect(local_keys, local_config.equal(resolved_config));
}
all_keys = intersect(all_keys, m_keys);
m_null_keys = substruct(substruct(all_keys, global_keys), local_keys);