mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-14 16:02:55 +00:00
Update from FS at b3c41fda4.
Slicing
- align merge_segmented_layers shape with FS apply_mm_segmentation
(size = num_facets_states, loop from 0, no -1 shift); painted mixed
regions were previously attributed to filament_id-1 of intent.
apply_fuzzy_skin_segmentation reads channel 1;
apply_mixed_surface_indentation uses segmentation_channel_filament_id
- port apply_mixed_surface_indentation, apply_mixed_component_surface_offsets,
apply_mixed_region_surface_offsets, apply_surface_emboss_mixed_region_override,
plus surface_emboss_mixed_* debug subsystem
- refactor apply_mm_segmentation (by-value MM, bias_mode, surface-type-
preserving intersection, region normalization, post-MM dump); hoist MM
segmentation into slice_volumes so mixed apply_* flow can mutate it
- restore clear_local_z_plan() invalidation hooks
(PrintObject.cpp:805/1264/1286)
GCode
- add LayerTools::preserve_extruder_order, honored by collect_extruders,
both reorder_extruders overloads, and
reorder_filaments_for_minimum_flush_volume; helpers
append_unique_preserve_order / remove_duplicates_preserve_order
- wire MixedFilamentManager::ordered_perimeter_extruders for grouped
manual-pattern walls; set preserve_extruder_order when >= 2
- mixed-aware support: layer_height set for support-only layers,
ExtrusionRole-based has_support/has_interface with erMixed short-
circuit, support_filament / support_interface_filament routed through
resolve_mixed
Print
- materialize mixed_filament_pointillism_{pixel_size,line_gap} in
PrintApply's option-tracking block so in-session edits diff correctly
GUI
- Tab::on_value_change: dithering_local_z_mode cascading clears, 17-key
project_config sync, update_mixed_filament_panel(false) on
mixed_filament_component_bias_enabled change
- GUI_Factories: physical_filaments_count, ui_ordered_filament_ids,
filament_menu_item_name; filaments_count includes enabled mixed
virtuals; right-click 'Change filament' submenus iterate UI-ordered IDs
Tests
- sentinel asserting MultiMaterialSegmentation uses FS-aligned shape
(39 -> 40)
This commit is contained in:
@@ -90,6 +90,12 @@ bool has_grouped_manual_pattern(const MixedFilamentManager *mixed_mgr,
|
||||
return normalized.find(',') != std::string::npos;
|
||||
}
|
||||
|
||||
void append_unique_preserve_order(std::vector<unsigned int> &dst, unsigned int value)
|
||||
{
|
||||
if (std::find(dst.begin(), dst.end(), value) == dst.end())
|
||||
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;
|
||||
@@ -141,6 +147,15 @@ unsigned int grouped_manual_pattern_infill_filament_1based(const LayerTools& la
|
||||
float(layer_tools.layer_height));
|
||||
}
|
||||
|
||||
void remove_duplicates_preserve_order(std::vector<unsigned int> &values)
|
||||
{
|
||||
std::vector<unsigned int> ordered;
|
||||
ordered.reserve(values.size());
|
||||
for (unsigned int value : values)
|
||||
append_unique_preserve_order(ordered, value);
|
||||
values = std::move(ordered);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -323,27 +338,29 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector<unsigned int>& too
|
||||
// Reorder the extruders of first layer
|
||||
{
|
||||
LayerTools& lt = m_layer_tools[0];
|
||||
std::vector<unsigned int> layer0_extruders = lt.extruders;
|
||||
lt.extruders.clear();
|
||||
for (unsigned int extruder_id : tool_order_layer0) {
|
||||
auto iter = std::find(layer0_extruders.begin(), layer0_extruders.end(), extruder_id);
|
||||
if (iter != layer0_extruders.end()) {
|
||||
lt.extruders.push_back(extruder_id);
|
||||
*iter = (unsigned int)-1;
|
||||
if (!lt.preserve_extruder_order) {
|
||||
std::vector<unsigned int> layer0_extruders = lt.extruders;
|
||||
lt.extruders.clear();
|
||||
for (unsigned int extruder_id : tool_order_layer0) {
|
||||
auto iter = std::find(layer0_extruders.begin(), layer0_extruders.end(), extruder_id);
|
||||
if (iter != layer0_extruders.end()) {
|
||||
lt.extruders.push_back(extruder_id);
|
||||
*iter = (unsigned int)-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int extruder_id : layer0_extruders) {
|
||||
if (extruder_id == 0)
|
||||
continue;
|
||||
for (unsigned int extruder_id : layer0_extruders) {
|
||||
if (extruder_id == 0)
|
||||
continue;
|
||||
|
||||
if (extruder_id != (unsigned int)-1)
|
||||
lt.extruders.push_back(extruder_id);
|
||||
}
|
||||
if (extruder_id != (unsigned int)-1)
|
||||
lt.extruders.push_back(extruder_id);
|
||||
}
|
||||
|
||||
// all extruders are zero
|
||||
if (lt.extruders.empty()) {
|
||||
lt.extruders.push_back(tool_order_layer0[0]);
|
||||
// all extruders are zero
|
||||
if (lt.extruders.empty()) {
|
||||
lt.extruders.push_back(tool_order_layer0[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,6 +376,10 @@ void ToolOrdering::handle_dontcare_extruder(const std::vector<unsigned int>& too
|
||||
if (lt.extruders.front() == 0)
|
||||
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
|
||||
lt.extruders.erase(lt.extruders.begin());
|
||||
if (lt.preserve_extruder_order) {
|
||||
last_extruder_id = lt.extruders.back();
|
||||
continue;
|
||||
}
|
||||
// Reorder the extruders to start with the last one.
|
||||
for (size_t i = 1; i < lt.extruders.size(); ++i)
|
||||
if (lt.extruders[i] == last_extruder_id) {
|
||||
@@ -413,6 +434,10 @@ void ToolOrdering::handle_dontcare_extruder(unsigned int last_extruder_id)
|
||||
if (lt.extruders.front() == 0)
|
||||
// Pop the "don't care" extruder, the "don't care" region will be merged with the next one.
|
||||
lt.extruders.erase(lt.extruders.begin());
|
||||
if (lt.preserve_extruder_order) {
|
||||
last_extruder_id = lt.extruders.back();
|
||||
continue;
|
||||
}
|
||||
// Reorder the extruders to start with the last one.
|
||||
for (size_t i = 1; i < lt.extruders.size(); ++ i)
|
||||
if (lt.extruders[i] == last_extruder_id) {
|
||||
@@ -874,19 +899,40 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
||||
}
|
||||
|
||||
if (something_nonoverriddable){
|
||||
if (extruder_override == 0) {
|
||||
layer_tools.extruders.emplace_back(layer_tools.wall_filament(region) + 1);
|
||||
if (layerCount == 0) {
|
||||
firstLayerExtruders.emplace_back(layer_tools.wall_filament(region) + 1);
|
||||
const unsigned int configured_wall = (extruder_override == 0)
|
||||
? region.config().wall_filament.value
|
||||
: extruder_override;
|
||||
unsigned int wall_ext = resolve_mixed(configured_wall,
|
||||
layerCount,
|
||||
float(layer->print_z),
|
||||
float(layer->height));
|
||||
const unsigned int grouped_id =
|
||||
grouped_manual_pattern_mixed_filament_id_for_layer(layer_tools, configured_wall);
|
||||
if (grouped_id != 0) {
|
||||
const std::vector<unsigned int> ordered =
|
||||
m_mixed_mgr->ordered_perimeter_extruders(grouped_id,
|
||||
m_num_physical,
|
||||
layerCount,
|
||||
float(layer->print_z),
|
||||
float(layer->height));
|
||||
if (!ordered.empty()) {
|
||||
if (ordered.size() >= 2)
|
||||
layer_tools.preserve_extruder_order = true;
|
||||
for (unsigned int extruder_id : ordered) {
|
||||
layer_tools.extruders.emplace_back(extruder_id);
|
||||
if (layerCount == 0 &&
|
||||
std::find(firstLayerExtruders.begin(), firstLayerExtruders.end(), int(extruder_id)) == firstLayerExtruders.end())
|
||||
firstLayerExtruders.emplace_back(int(extruder_id));
|
||||
}
|
||||
} else {
|
||||
layer_tools.extruders.emplace_back(wall_ext);
|
||||
if (layerCount == 0)
|
||||
firstLayerExtruders.emplace_back(wall_ext);
|
||||
}
|
||||
} else {
|
||||
const unsigned int resolved = resolve_mixed(extruder_override,
|
||||
layerCount,
|
||||
float(layer->print_z),
|
||||
float(layer->height));
|
||||
layer_tools.extruders.emplace_back(resolved);
|
||||
layer_tools.extruders.emplace_back(wall_ext);
|
||||
if (layerCount == 0)
|
||||
firstLayerExtruders.emplace_back(resolved);
|
||||
firstLayerExtruders.emplace_back(wall_ext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,19 +981,24 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
||||
|
||||
// Collect the support extruders.
|
||||
for (auto support_layer : object.support_layers()) {
|
||||
LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z);
|
||||
ExtrusionRole role = support_layer->support_fills.role();
|
||||
bool has_support = false;
|
||||
bool has_interface = false;
|
||||
for (const ExtrusionEntity *ee : support_layer->support_fills.entities) {
|
||||
ExtrusionRole er = ee->role();
|
||||
if (er == erSupportMaterial || er == erSupportTransition) has_support = true;
|
||||
if (er == erSupportMaterialInterface) has_interface = true;
|
||||
if (has_support && has_interface) break;
|
||||
}
|
||||
unsigned int extruder_support = object.config().support_filament.value;
|
||||
unsigned int extruder_interface = object.config().support_interface_filament.value;
|
||||
LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z);
|
||||
layer_tools.layer_height = support_layer->height;
|
||||
ExtrusionRole role = support_layer->support_fills.role();
|
||||
bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition;
|
||||
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
|
||||
|
||||
unsigned int extruder_support = resolve_mixed(object.config().support_filament.value,
|
||||
layer_tools.layer_index,
|
||||
float(support_layer->print_z),
|
||||
float(support_layer->height));
|
||||
unsigned int extruder_interface = resolve_mixed(object.config().support_interface_filament.value,
|
||||
layer_tools.layer_index,
|
||||
float(support_layer->print_z),
|
||||
float(support_layer->height));
|
||||
|
||||
if (has_support) {
|
||||
// BP-only fallback: when support_filament is unset and an interface
|
||||
// exists, pick the lowest-flush non-soluble body extruder.
|
||||
if (extruder_support > 0 || !has_interface || extruder_interface == 0 || layer_tools.has_object)
|
||||
layer_tools.extruders.push_back(extruder_support);
|
||||
else {
|
||||
@@ -956,7 +1007,6 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
||||
std::vector<float> flush_matrix(
|
||||
cast<float>(get_flush_volumes_matrix(object.print()->config().flush_volumes_matrix.values, 0, object.print()->config().nozzle_diameter.values.size())));
|
||||
const unsigned int number_of_extruders = (unsigned int) (sqrt(flush_matrix.size()) + EPSILON);
|
||||
// Extract purging volumes for each extruder pair:
|
||||
std::vector<std::vector<float>> wipe_volumes;
|
||||
for (unsigned int i = 0; i < number_of_extruders; ++i)
|
||||
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders, flush_matrix.begin() + (i + 1) * number_of_extruders));
|
||||
@@ -983,8 +1033,10 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
||||
}
|
||||
|
||||
for (auto& layer : m_layer_tools) {
|
||||
// Sort and remove duplicates
|
||||
sort_remove_duplicates(layer.extruders);
|
||||
if (layer.preserve_extruder_order)
|
||||
remove_duplicates_preserve_order(layer.extruders);
|
||||
else
|
||||
sort_remove_duplicates(layer.extruders);
|
||||
|
||||
// make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
|
||||
if (layer.extruders.empty() && layer.has_object)
|
||||
@@ -1539,6 +1591,9 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
||||
&filament_sequences
|
||||
);
|
||||
|
||||
// TODO(fs-port): for layers with preserve_extruder_order=true the stats
|
||||
// here reflect the optimized sequence; the guarded writeback below keeps
|
||||
// the original ordering. UI-only divergence — see line ~1610 below.
|
||||
auto curr_flush_info = calc_filament_change_info_by_toolorder(print_config, filament_maps, nozzle_flush_mtx, filament_sequences);
|
||||
if (nozzle_nums <= 1)
|
||||
m_stats_by_single_extruder = curr_flush_info;
|
||||
@@ -1580,8 +1635,19 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < filament_sequences.size(); ++i)
|
||||
for (size_t i = 0; i < filament_sequences.size(); ++i) {
|
||||
// FS preserve_extruder_order guard: keep the layer's existing extruder
|
||||
// ordering by skipping writeback of the optimized sequence.
|
||||
//
|
||||
// Note: BP runs the optimizer (and updates stats) BEFORE this writeback
|
||||
// because the optimizer is a free function in ToolOrderUtils.cpp without
|
||||
// LayerTools access. Stats may diverge from g-code reality on
|
||||
// preserve-order layers. See FS ToolOrdering.cpp:1156-1159 for the
|
||||
// upstream behavior that guards earlier in the call chain.
|
||||
if (m_layer_tools[i].preserve_extruder_order)
|
||||
continue;
|
||||
m_layer_tools[i].extruders = std::move(filament_sequences[i]);
|
||||
}
|
||||
}
|
||||
// Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed.
|
||||
void ToolOrdering::mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height)
|
||||
|
||||
@@ -157,6 +157,9 @@ public:
|
||||
bool has_support = false;
|
||||
// Zero based extruder IDs, ordered to minimize tool switches.
|
||||
std::vector<unsigned int> extruders;
|
||||
// When set, downstream reorder passes leave this layer's extruder
|
||||
// sequence in place (used by grouped manual mixed-filament patterns).
|
||||
bool preserve_extruder_order = false;
|
||||
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
|
||||
// If not overriden, it is set to 0.
|
||||
unsigned int extruder_override = 0;
|
||||
|
||||
Reference in New Issue
Block a user