mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 10:51:22 +00:00
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:
+2
-1
@@ -41,4 +41,5 @@ resources/profiles/user/default
|
||||
deps_src/build/
|
||||
.claude/
|
||||
.omc
|
||||
nul
|
||||
nul
|
||||
.codex_tmp
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 ®ion, 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 ®ion)
|
||||
{
|
||||
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 ®ion)
|
||||
{
|
||||
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 ®ion, 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 ®ion) const
|
||||
|
||||
unsigned int LayerTools::sparse_infill_filament(const PrintRegion ®ion) 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++;
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ®ion_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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user