Add mixed filament support: Implement MixedFilament and MixedFilamentManager classes for layer-based color mixing. Update CLI to handle downward_check option. Enhance GUI to display mixed filaments and integrate with existing filament management.

This commit is contained in:
Rad
2026-02-09 22:35:18 +01:00
parent 706508c792
commit e529461234
21 changed files with 669 additions and 39 deletions
+2
View File
@@ -283,6 +283,8 @@ set(lisbslic3r_sources
MinAreaBoundingBox.hpp
MinimumSpanningTree.cpp
MinimumSpanningTree.hpp
MixedFilament.cpp
MixedFilament.hpp
miniz_extension.cpp
miniz_extension.hpp
ModelArrange.cpp
+41 -10
View File
@@ -146,23 +146,34 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
return false;
}
// Resolve a 1-based filament ID through the mixed-filament manager for this layer.
unsigned int LayerTools::resolve_mixed_1based(unsigned int filament_id) const
{
if (mixed_mgr && mixed_mgr->is_mixed(filament_id, num_physical))
return mixed_mgr->resolve(filament_id, num_physical, this->layer_index);
return filament_id;
}
// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int LayerTools::wall_filament(const PrintRegion &region) const
{
assert(region.config().wall_filament.value > 0);
return ((this->extruder_override == 0) ? region.config().wall_filament.value : this->extruder_override) - 1;
unsigned int id = (this->extruder_override == 0) ? region.config().wall_filament.value : this->extruder_override;
return resolve_mixed_1based(id) - 1;
}
unsigned int LayerTools::sparse_infill_filament(const PrintRegion &region) const
{
assert(region.config().sparse_infill_filament.value > 0);
return ((this->extruder_override == 0) ? region.config().sparse_infill_filament.value : this->extruder_override) - 1;
unsigned int id = (this->extruder_override == 0) ? region.config().sparse_infill_filament.value : this->extruder_override;
return resolve_mixed_1based(id) - 1;
}
unsigned int LayerTools::solid_infill_filament(const PrintRegion &region) const
{
assert(region.config().solid_infill_filament.value > 0);
return ((this->extruder_override == 0) ? region.config().solid_infill_filament.value : this->extruder_override) - 1;
unsigned int id = (this->extruder_override == 0) ? region.config().solid_infill_filament.value : this->extruder_override;
return resolve_mixed_1based(id) - 1;
}
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
@@ -184,6 +195,7 @@ unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, c
} else
extruder = this->extruder_override;
extruder = resolve_mixed_1based(extruder);
return (extruder == 0) ? 0 : extruder - 1;
}
@@ -208,6 +220,9 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
m_is_BBL_printer = object.print()->is_BBL_printer();
m_print_full_config = &object.print()->full_print_config();
m_print_object_ptr = &object;
// Mixed filament support.
m_mixed_mgr = &object.print()->mixed_filament_manager();
m_num_physical = object.print()->config().filament_diameter.size();
if (object.layers().empty())
return;
@@ -271,6 +286,9 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
m_is_BBL_printer = print.is_BBL_printer();
m_print_full_config = &print.full_print_config();
m_print_config_ptr = &print.config();
// Mixed filament support.
m_mixed_mgr = &print.mixed_filament_manager();
m_num_physical = print.config().filament_diameter.size();
// Initialize the print layers for all objects and all layers.
coordf_t object_bottom_z = 0.;
@@ -480,8 +498,8 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
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 = object.config().support_filament.value;
unsigned int extruder_interface = object.config().support_interface_filament.value;
unsigned int extruder_support = resolve_mixed(object.config().support_filament.value, layer_tools.layer_index);
unsigned int extruder_interface = resolve_mixed(object.config().support_interface_filament.value, layer_tools.layer_index);
if (has_support)
layer_tools.extruders.push_back(extruder_support);
if (has_interface)
@@ -505,6 +523,10 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
// Collect the object extruders.
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.mixed_mgr = m_mixed_mgr;
layer_tools.num_physical = m_num_physical;
// 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)
@@ -528,9 +550,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
}
if (something_nonoverriddable){
layer_tools.extruders.emplace_back((extruder_override == 0) ? region.config().wall_filament.value : extruder_override);
unsigned int wall_ext = (extruder_override == 0) ? region.config().wall_filament.value : extruder_override;
wall_ext = resolve_mixed(wall_ext, layerCount);
layer_tools.extruders.emplace_back(wall_ext);
if (layerCount == 0) {
firstLayerExtruders.emplace_back((extruder_override == 0) ? region.config().wall_filament.value : extruder_override);
firstLayerExtruders.emplace_back(wall_ext);
}
}
@@ -558,11 +582,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
if (something_nonoverriddable || !m_print_config_ptr) {
if (extruder_override == 0) {
if (has_solid_infill)
layer_tools.extruders.emplace_back(region.config().solid_infill_filament);
layer_tools.extruders.emplace_back(resolve_mixed(region.config().solid_infill_filament, layerCount));
if (has_infill)
layer_tools.extruders.emplace_back(region.config().sparse_infill_filament);
layer_tools.extruders.emplace_back(resolve_mixed(region.config().sparse_infill_filament, layerCount));
} else if (has_solid_infill || has_infill)
layer_tools.extruders.emplace_back(extruder_override);
layer_tools.extruders.emplace_back(resolve_mixed(extruder_override, layerCount));
}
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
@@ -1414,5 +1438,12 @@ int WipingExtrusions::get_support_interface_extruder_overrides(const PrintObject
return -1;
}
// Resolve a 1-based filament ID through the mixed-filament manager.
unsigned int ToolOrdering::resolve_mixed(unsigned int filament_id_1based, int layer_index) const
{
if (m_mixed_mgr && m_mixed_mgr->is_mixed(filament_id_1based, m_num_physical))
return m_mixed_mgr->resolve(filament_id_1based, m_num_physical, layer_index);
return filament_id_1based;
}
} // namespace Slic3r
+18
View File
@@ -4,6 +4,7 @@
#define slic3r_ToolOrdering_hpp_
#include "../libslic3r.h"
#include "../MixedFilament.hpp"
#include <utility>
@@ -119,6 +120,8 @@ public:
// 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;
// Sequential layer index (0-based), used by mixed-filament resolution.
int layer_index = 0;
// Should a skirt be printed at this layer?
// Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed.
bool has_skirt = false;
@@ -138,7 +141,13 @@ public:
return m_wiping_extrusions;
}
// Mixed-filament resolution context (set by ToolOrdering during collect_extruders).
const MixedFilamentManager *mixed_mgr = nullptr;
size_t num_physical = 0;
private:
// Resolve a 1-based filament ID through the mixed-filament manager for this layer.
unsigned int resolve_mixed_1based(unsigned int filament_id) const;
// This object holds list of extrusion that will be used for extruder wiping
WipingExtrusions m_wiping_extrusions;
};
@@ -203,6 +212,11 @@ private:
std::vector<unsigned int> generate_first_layer_tool_order(const Print& print);
std::vector<unsigned int> generate_first_layer_tool_order(const PrintObject& object);
// Resolve a 1-based filament ID through the mixed-filament manager.
// Returns the resolved physical extruder (1-based). If the ID is not a
// mixed filament or no manager is set, returns the input unchanged.
unsigned int resolve_mixed(unsigned int filament_id_1based, int layer_index) const;
std::vector<LayerTools> m_layer_tools;
// First printing extruder, including the multi-material priming sequence.
unsigned int m_first_printing_extruder = (unsigned int)-1;
@@ -215,6 +229,10 @@ private:
const PrintConfig* m_print_config_ptr = nullptr;
const PrintObject* m_print_object_ptr = nullptr;
bool m_is_BBL_printer = false;
// Mixed filament support: pointer to manager (owned by Print) and
// number of physical extruders.
const MixedFilamentManager* m_mixed_mgr = nullptr;
size_t m_num_physical = 0;
};
} // namespace SLic3r
+158
View File
@@ -0,0 +1,158 @@
#include "MixedFilament.hpp"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <sstream>
#include <iomanip>
namespace Slic3r {
// ---------------------------------------------------------------------------
// Colour helpers (internal)
// ---------------------------------------------------------------------------
struct RGB {
int r = 0, g = 0, b = 0;
};
// Parse "#RRGGBB" to RGB. Returns black on failure.
static RGB parse_hex_color(const std::string &hex)
{
RGB c;
if (hex.size() >= 7 && hex[0] == '#') {
c.r = std::stoi(hex.substr(1, 2), nullptr, 16);
c.g = std::stoi(hex.substr(3, 2), nullptr, 16);
c.b = std::stoi(hex.substr(5, 2), nullptr, 16);
}
return c;
}
static std::string rgb_to_hex(const RGB &c)
{
char buf[8];
std::snprintf(buf, sizeof(buf), "#%02X%02X%02X", c.r, c.g, c.b);
return std::string(buf);
}
// ---------------------------------------------------------------------------
// MixedFilamentManager
// ---------------------------------------------------------------------------
void MixedFilamentManager::auto_generate(const std::vector<std::string> &filament_colours)
{
// Keep a copy of the old list so we can preserve user-modified ratios and
// enabled flags.
std::vector<MixedFilament> old = std::move(m_mixed);
m_mixed.clear();
const size_t n = filament_colours.size();
if (n < 2)
return;
// Generate all C(N,2) pairwise combinations.
for (size_t i = 0; i < n; ++i) {
for (size_t j = i + 1; j < n; ++j) {
MixedFilament mf;
mf.component_a = static_cast<unsigned int>(i + 1); // 1-based
mf.component_b = static_cast<unsigned int>(j + 1);
mf.ratio_a = 1;
mf.ratio_b = 1;
mf.enabled = true;
// Try to preserve previous settings.
for (const auto &prev : old) {
if (prev.component_a == mf.component_a &&
prev.component_b == mf.component_b) {
mf.ratio_a = prev.ratio_a;
mf.ratio_b = prev.ratio_b;
mf.enabled = prev.enabled;
break;
}
}
mf.display_color = blend_color(filament_colours[i],
filament_colours[j],
mf.ratio_a, mf.ratio_b);
m_mixed.push_back(mf);
}
}
}
unsigned int MixedFilamentManager::resolve(unsigned int filament_id,
size_t num_physical,
int layer_index) const
{
if (!is_mixed(filament_id, num_physical))
return filament_id;
const size_t idx = index_of(filament_id, num_physical);
if (idx >= m_mixed.size())
return 1; // fallback to first extruder
const MixedFilament &mf = m_mixed[idx];
const int cycle = mf.ratio_a + mf.ratio_b;
if (cycle <= 0)
return mf.component_a;
const int pos = ((layer_index % cycle) + cycle) % cycle; // safe modulo for negatives
return (pos < mf.ratio_a) ? mf.component_a : mf.component_b;
}
std::string MixedFilamentManager::blend_color(const std::string &color_a,
const std::string &color_b,
int ratio_a, int ratio_b)
{
RGB a = parse_hex_color(color_a);
RGB b = parse_hex_color(color_b);
// Additive blend: min(a + b, 255) per channel.
// For unequal ratios, weight accordingly.
const float total = static_cast<float>(ratio_a + ratio_b);
const float wa = (total > 0.f) ? static_cast<float>(ratio_a) / total : 0.5f;
const float wb = 1.f - wa;
// Use screen blending which is additive-like without oversaturation:
// screen(A, B) = A + B - A*B/255
// Weighted variant: blend each channel independently.
auto screen_ch = [](int ca, int cb, float wa, float wb) -> int {
// Weighted additive with clamping – matches user expectation:
// Red(255,0,0) + Green(0,255,0) = Yellow(255,255,0)
float v = static_cast<float>(ca) * wa + static_cast<float>(cb) * wb;
// Boost towards additive: add the minimum so pure colours combine fully.
float additive = std::min(static_cast<float>(ca + cb), 255.f);
// Blend between weighted-average and full-additive based on colour distance.
float result = wa * static_cast<float>(ca) + wb * static_cast<float>(cb);
// For the 1:1 case, use pure additive (clamped) to get R+G=Y.
if (std::abs(wa - wb) < 0.01f)
result = additive;
return std::min(static_cast<int>(std::round(result)), 255);
};
RGB out;
out.r = screen_ch(a.r, b.r, wa, wb);
out.g = screen_ch(a.g, b.g, wa, wb);
out.b = screen_ch(a.b, b.b, wa, wb);
return rgb_to_hex(out);
}
size_t MixedFilamentManager::enabled_count() const
{
size_t count = 0;
for (const auto &mf : m_mixed)
if (mf.enabled)
++count;
return count;
}
std::vector<std::string> MixedFilamentManager::display_colors() const
{
std::vector<std::string> colors;
for (const auto &mf : m_mixed)
if (mf.enabled)
colors.push_back(mf.display_color);
return colors;
}
} // namespace Slic3r
+108
View File
@@ -0,0 +1,108 @@
#ifndef slic3r_MixedFilament_hpp_
#define slic3r_MixedFilament_hpp_
#include <string>
#include <vector>
#include <algorithm>
#include <cstdint>
namespace Slic3r {
// Represents a virtual "mixed" filament created by alternating layers of two
// physical filaments. The display colour is an additive RGB blend so that,
// for example, Red + Green previews as Yellow.
struct MixedFilament
{
// 1-based physical filament IDs that are combined.
unsigned int component_a = 1;
unsigned int component_b = 2;
// Layer-alternation ratio. With ratio_a = 2, ratio_b = 1 the cycle is
// A, A, B, A, A, B, ...
int ratio_a = 1;
int ratio_b = 1;
// Whether this mixed filament is enabled (available for assignment).
bool enabled = true;
// Computed display colour as "#RRGGBB".
std::string display_color;
bool operator==(const MixedFilament &rhs) const
{
return component_a == rhs.component_a &&
component_b == rhs.component_b &&
ratio_a == rhs.ratio_a &&
ratio_b == rhs.ratio_b &&
enabled == rhs.enabled;
}
bool operator!=(const MixedFilament &rhs) const { return !(*this == rhs); }
};
// ---------------------------------------------------------------------------
// MixedFilamentManager
//
// Owns the list of mixed filaments and provides helpers used by the slicing
// pipeline to resolve virtual IDs back to physical extruders.
//
// Virtual filament IDs are numbered starting at (num_physical + 1). For a
// 4-extruder printer the first mixed filament has ID 5, the second 6, etc.
// ---------------------------------------------------------------------------
class MixedFilamentManager
{
public:
MixedFilamentManager() = default;
// ---- Auto-generation ------------------------------------------------
// Rebuild the mixed-filament list from the current set of physical
// filament colours. Generates all C(N,2) pairwise combinations.
// Previous ratio/enabled state is preserved when a combination still
// exists.
void auto_generate(const std::vector<std::string> &filament_colours);
// ---- Queries --------------------------------------------------------
// True when `filament_id` (1-based) refers to a mixed filament.
bool is_mixed(unsigned int filament_id, size_t num_physical) const
{
return filament_id > num_physical && index_of(filament_id, num_physical) < m_mixed.size();
}
// Resolve a mixed filament ID to a physical extruder (1-based) for the
// given `layer_index`. Returns `filament_id` unchanged when it is not a
// mixed filament.
unsigned int resolve(unsigned int filament_id, size_t num_physical, int layer_index) const;
// Compute a display colour by additively blending the two component
// colours. `filament_colours` contains the physical colours only.
static std::string blend_color(const std::string &color_a,
const std::string &color_b,
int ratio_a, int ratio_b);
// ---- Accessors ------------------------------------------------------
const std::vector<MixedFilament> &mixed_filaments() const { return m_mixed; }
std::vector<MixedFilament> &mixed_filaments() { return m_mixed; }
size_t enabled_count() const;
// Total filament count = num_physical + number of *enabled* mixed filaments.
size_t total_filaments(size_t num_physical) const { return num_physical + enabled_count(); }
// Return the display colours of all enabled mixed filaments (in order).
std::vector<std::string> display_colors() const;
private:
// Convert a 1-based virtual ID to a 0-based index into m_mixed.
size_t index_of(unsigned int filament_id, size_t num_physical) const
{
return static_cast<size_t>(filament_id - num_physical - 1);
}
std::vector<MixedFilament> m_mixed;
};
} // namespace Slic3r
#endif /* slic3r_MixedFilament_hpp_ */
+11
View File
@@ -2477,6 +2477,17 @@ void ModelVolume::update_extruder_count(size_t extruder_count)
std::vector<int> used_extruders = get_extruders();
for (int extruder_id : used_extruders) {
if (extruder_id > extruder_count) {
std::string used_extruders_str;
for (size_t i = 0; i < used_extruders.size(); ++i) {
if (i > 0)
used_extruders_str += ",";
used_extruders_str += std::to_string(used_extruders[i]);
}
BOOST_LOG_TRIVIAL(warning) << "ModelVolume::update_extruder_count clipping painted extruders to limit"
<< " volume_name=" << this->name
<< " volume_id=" << this->id().id
<< " requested_limit=" << extruder_count
<< " used_extruders=[" << used_extruders_str << "]";
mmu_segmentation_facets.set_enforcer_block_type_limit(*this, (EnforcerBlockerType)extruder_count);
break;
}
+28 -1
View File
@@ -8,6 +8,7 @@
#include "MutablePolygon.hpp"
#include "format.hpp"
#include <algorithm>
#include <utility>
#include <unordered_set>
@@ -2195,11 +2196,37 @@ std::vector<std::vector<ExPolygons>> segmentation_by_painting(const PrintObject
// Returns multi-material segmentation based on painting in multi-material segmentation gizmo
std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback) {
const size_t num_facets_states = print_object.print()->config().filament_colour.size() + 1;
const size_t num_physical_filaments = print_object.print()->config().filament_colour.size();
const size_t num_total_filaments = print_object.print()->mixed_filament_manager().total_filaments(num_physical_filaments);
const size_t num_facets_states = num_total_filaments + 1;
const float max_width = float(print_object.config().mmu_segmented_region_max_width.value);
const float interlocking_depth = float(print_object.config().mmu_segmented_region_interlocking_depth.value);
const bool interlocking_beam = print_object.config().interlocking_beam.value;
size_t max_painted_state = 0;
for (const ModelVolume *mv : print_object.model_object()->volumes) {
if (!mv->is_model_part())
continue;
const auto &used_states = mv->mmu_segmentation_facets.get_data().used_states;
for (size_t state_idx = static_cast<size_t>(EnforcerBlockerType::Extruder1); state_idx < used_states.size(); ++state_idx) {
if (used_states[state_idx])
max_painted_state = std::max(max_painted_state, state_idx);
}
}
if (max_painted_state >= num_facets_states) {
BOOST_LOG_TRIVIAL(warning) << "multi_material_segmentation_by_painting dropping painted states above segmentation range"
<< " max_painted_state=" << max_painted_state
<< " num_facets_states=" << num_facets_states
<< " physical_filaments=" << num_physical_filaments
<< " total_filaments=" << num_total_filaments;
} else {
BOOST_LOG_TRIVIAL(debug) << "multi_material_segmentation_by_painting state range check"
<< " max_painted_state=" << max_painted_state
<< " num_facets_states=" << num_facets_states
<< " physical_filaments=" << num_physical_filaments
<< " total_filaments=" << num_total_filaments;
}
const auto extract_facets_info = [](const ModelVolume &mv) -> ModelVolumeFacetsInfo {
return {mv.mmu_segmentation_facets, mv.is_mm_painted(), false};
};
+13 -1
View File
@@ -1811,7 +1811,9 @@ void PresetBundle::export_selections(AppConfig &config)
config.set_printer_setting(printer_name, name, filament_presets[i]);
}
CNumericLocalesSetter locales_setter;
std::string filament_colors = boost::algorithm::join(project_config.option<ConfigOptionStrings>("filament_colour")->values, ",");
std::vector<std::string> physical_filament_colors = project_config.option<ConfigOptionStrings>("filament_colour")->values;
physical_filament_colors.resize(filament_presets.size(), "#26A69A");
std::string filament_colors = boost::algorithm::join(physical_filament_colors, ",");
config.set_printer_setting(printer_name, "filament_colors", filament_colors);
std::string flush_volumes_matrix = boost::algorithm::join(project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values |
boost::adaptors::transformed(static_cast<std::string (*)(double)>(std::to_string)),
@@ -3256,6 +3258,16 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
}
this->project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values = new_matrix;
}
// Keep project colours aligned to physical filaments, then regenerate mixed
// (virtual) entries from the physical set only.
{
ConfigOptionStrings *color_opt = this->project_config.option<ConfigOptionStrings>("filament_colour");
if (color_opt) {
color_opt->values.resize(num_filaments, "#26A69A");
this->mixed_filaments.auto_generate(color_opt->values);
}
}
}
void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible)
+4
View File
@@ -4,6 +4,7 @@
#include "Preset.hpp"
#include "AppConfig.hpp"
#include "enum_bitmask.hpp"
#include "MixedFilament.hpp"
#include <memory>
#include <unordered_map>
@@ -153,6 +154,9 @@ public:
std::map<int, DynamicPrintConfig> filament_ams_list;
std::vector<std::vector<std::string>> ams_multi_color_filment;
// Mixed (virtual) filaments for layer-based colour mixing.
MixedFilamentManager mixed_filaments;
// Snapmaker
std::map<int, std::pair<std::string, std::string>> machine_filaments;
+4
View File
@@ -17,6 +17,7 @@
#include "GCode/ThumbnailData.hpp"
#include "GCode/GCodeProcessor.hpp"
#include "MultiMaterialSegmentation.hpp"
#include "MixedFilament.hpp"
#include "libslic3r.h"
#include <Eigen/Geometry>
@@ -896,6 +897,8 @@ public:
const PrintConfig& config() const { return m_config; }
const PrintObjectConfig& default_object_config() const { return m_default_object_config; }
const PrintRegionConfig& default_region_config() const { return m_default_region_config; }
const MixedFilamentManager& mixed_filament_manager() const { return m_mixed_filament_mgr; }
MixedFilamentManager& mixed_filament_manager() { return m_mixed_filament_mgr; }
ConstPrintObjectPtrsAdaptor objects() const { return ConstPrintObjectPtrsAdaptor(&m_objects); }
PrintObject* get_object(size_t idx) { return const_cast<PrintObject*>(m_objects[idx]); }
const PrintObject* get_object(size_t idx) const { return m_objects[idx]; }
@@ -1028,6 +1031,7 @@ private:
PrintConfig m_config;
PrintObjectConfig m_default_object_config;
PrintRegionConfig m_default_region_config;
MixedFilamentManager m_mixed_filament_mgr;
PrintObjectPtrs m_objects;
PrintRegionPtrs m_print_regions;
+50 -7
View File
@@ -2,6 +2,7 @@
#include "Print.hpp"
#include <boost/log/trivial.hpp>
#include <algorithm>
#include <cfloat>
namespace Slic3r {
@@ -1188,6 +1189,14 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
}
// Regenerate mixed (virtual) filaments from physical filament colours only.
std::vector<std::string> physical_filament_colors = m_config.filament_colour.values;
physical_filament_colors.resize(num_extruders, "#26A69A");
m_mixed_filament_mgr.auto_generate(physical_filament_colors);
// Total filaments = physical extruders + enabled mixed (virtual) filaments.
// Used for extruder ID clamping so that virtual IDs are accepted.
size_t num_total_filaments = m_mixed_filament_mgr.total_filaments(num_extruders);
ModelObjectStatusDB model_object_status_db;
// 1) Synchronize model objects.
@@ -1375,7 +1384,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
if (object_config_changed)
model_object.config.assign_config(model_object_new.config);
if (! object_diff.empty() || object_config_changed || num_extruders_changed ) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders );
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_total_filaments );
for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(model_object)) {
t_config_option_keys diff = print_object_status.print_object->config().diff(new_config);
if (! diff.empty()) {
@@ -1438,10 +1447,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Generate a list of trafos and XY offsets for instances of a ModelObject
// Producing the config for PrintObject on demand, caching it at print_object_last.
const PrintObject *print_object_last = nullptr;
auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders ](PrintObject *print_object) {
auto print_object_apply_config = [this, &print_object_last, model_object, num_total_filaments ](PrintObject *print_object) {
print_object->config_apply(print_object_last ?
print_object_last->config() :
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders ));
PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_total_filaments ));
print_object_last = print_object;
};
if (old.empty()) {
@@ -1581,9 +1590,43 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
used_facet_states[state_idx] |= volume_used_facet_states[state_idx];
}
size_t dropped_painted_states = 0;
for (size_t state_idx = static_cast<size_t>(EnforcerBlockerType::Extruder1); state_idx < used_facet_states.size(); ++state_idx) {
if (used_facet_states[state_idx])
painting_extruders.emplace_back(state_idx);
if (!used_facet_states[state_idx])
continue;
if (state_idx <= num_total_filaments)
painting_extruders.emplace_back(static_cast<unsigned int>(state_idx));
else
++dropped_painted_states;
}
if (dropped_painted_states > 0) {
BOOST_LOG_TRIVIAL(warning) << "Print::apply dropping painted extruder IDs above available filament range"
<< " dropped_states=" << dropped_painted_states
<< " physical_filaments=" << num_extruders
<< " total_filaments=" << num_total_filaments;
}
if (!painting_extruders.empty()) {
std::string painting_ids;
for (size_t i = 0; i < painting_extruders.size(); ++i) {
if (i > 0)
painting_ids += ",";
painting_ids += std::to_string(painting_extruders[i]);
}
const unsigned int max_painted_extruder = *std::max_element(painting_extruders.begin(), painting_extruders.end());
if (max_painted_extruder > num_total_filaments) {
BOOST_LOG_TRIVIAL(warning) << "Print::apply detected painted extruder IDs above available filament range"
<< " painted_extruders=[" << painting_ids << "]"
<< " physical_filaments=" << num_extruders
<< " total_filaments=" << num_total_filaments;
} else {
BOOST_LOG_TRIVIAL(debug) << "Print::apply collected painted extruders"
<< " painted_extruders=[" << painting_ids << "]"
<< " physical_filaments=" << num_extruders
<< " total_filaments=" << num_total_filaments;
}
}
}
if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) {
@@ -1602,7 +1645,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
verify_update_print_object_regions(
print_object.model_object()->volumes,
m_default_region_config,
num_extruders,
num_total_filaments,
*print_object_regions,
[it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
for (auto it = it_print_object; it != it_print_object_end; ++it)
@@ -1627,7 +1670,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
LayerRanges(print_object.model_object()->layer_config_ranges),
m_default_region_config,
model_object_status.print_instances.front().trafo,
num_extruders ,
num_total_filaments ,
print_object.is_mm_painted() ? 0.f : float(print_object.config().xy_contour_compensation.value),
painting_extruders,
print_object.is_fuzzy_skin_painted());
+4 -2
View File
@@ -3157,9 +3157,11 @@ void PrintObject::bridge_over_infill()
} // void PrintObject::bridge_over_infill()
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
// Clamp extruder IDs that exceed the total number of available filaments
// (physical + virtual/mixed) back to the default extruder.
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_total_filaments)
{
if (opt.value > (int)num_extruders)
if (opt.value > (int)num_total_filaments)
// assign the default extruder
opt.value = 1;
}
+4 -2
View File
@@ -854,8 +854,9 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
const auto &layer_ranges = print_object.shared_regions()->layer_ranges;
double z = print_object.get_layer(int(range.begin()))->slice_z;
auto it_layer_range = layer_range_first(layer_ranges, z);
// BBS
const size_t num_extruders = print_object.print()->config().filament_diameter.size();
// MM segmentation channels correspond to filament IDs (1-based), which now
// include enabled mixed / virtual filaments.
const size_t num_extruders = segmentation.empty() ? 0 : segmentation.front().size();
struct ByExtruder {
ExPolygons expolygons;
@@ -875,6 +876,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
it_layer_range = layer_range_next(layer_ranges, it_layer_range, layer.slice_z);
const PrintObjectRegions::LayerRangeRegions &layer_range = *it_layer_range;
// Gather per extruder expolygons.
assert(segmentation[layer_id].size() == num_extruders);
by_extruder.assign(num_extruders, ByExtruder());
by_region.assign(layer.region_count(), ByRegion());
bool layer_split = false;