mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 13:32:44 +00:00
Update README and implement dithering features: Change download link in README, add mixed filament management enhancements including filament removal and layer height adjustments for dithering in the MixedFilamentManager. Introduce new configuration options for dithering in the PrintConfig and GUI, and ensure proper handling of mixed filament states during filament deletion.
This commit is contained in:
@@ -27,7 +27,7 @@
|
||||
# Download
|
||||
|
||||
### Stable Release
|
||||
📥 **[Download the Latest Stable Release](https://github.com/Snapmaker/OrcaSlicer/releases/latest)**
|
||||
📥 **[Download the Latest Stable Release](https://github.com/ratdoux/OrcaSlicer-FullSpectrum/releases)**
|
||||
Visit our GitHub Releases page for the latest stable version of Full Spectrum, recommended for most users.
|
||||
|
||||
# Features
|
||||
@@ -147,6 +147,9 @@ Bambu Studio is forked from [PrusaSlicer](https://github.com/prusa3d/PrusaSlicer
|
||||
Orca Slicer incorporates a lot of features from SuperSlicer by @supermerill
|
||||
Orca Slicer's logo is designed by community member Justin Levine(@freejstnalxndr)
|
||||
|
||||
## Acknowledgements
|
||||
Special thanks to [u/Aceman11100](https://www.reddit.com/user/Aceman11100/) for the inspiration and idea behind the mixed-color filament feature!
|
||||
|
||||
|
||||
# License
|
||||
Full Spectrum is licensed under the GNU Affero General Public License, version 3. Full Spectrum is based on Snapmaker Orca.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <libslic3r.h>
|
||||
|
||||
@@ -24,6 +25,40 @@ namespace Slic3r {
|
||||
|
||||
const static bool g_wipe_into_objects = false;
|
||||
|
||||
namespace {
|
||||
|
||||
unsigned int resolve_mixed_with_layer_heights(const MixedFilamentManager *mixed_mgr,
|
||||
size_t num_physical,
|
||||
unsigned int filament_id_1based,
|
||||
int layer_index,
|
||||
float layer_height_a,
|
||||
float layer_height_b,
|
||||
float base_layer_height)
|
||||
{
|
||||
if (!(mixed_mgr && mixed_mgr->is_mixed(filament_id_1based, num_physical)))
|
||||
return filament_id_1based;
|
||||
|
||||
if (layer_height_a > 0.f || layer_height_b > 0.f) {
|
||||
const float safe_base = std::max<float>(0.01f, base_layer_height);
|
||||
const int ratio_a = std::max(1, int(std::lround((layer_height_a > 0.f ? layer_height_a : safe_base) / safe_base)));
|
||||
const int ratio_b = std::max(1, int(std::lround((layer_height_b > 0.f ? layer_height_b : safe_base) / safe_base)));
|
||||
const int cycle = ratio_a + ratio_b;
|
||||
|
||||
if (cycle > 0) {
|
||||
const size_t idx = static_cast<size_t>(filament_id_1based - num_physical - 1);
|
||||
const auto &mixed = mixed_mgr->mixed_filaments();
|
||||
if (idx < mixed.size()) {
|
||||
const int pos = ((layer_index % cycle) + cycle) % cycle;
|
||||
return pos < ratio_a ? mixed[idx].component_a : mixed[idx].component_b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mixed_mgr->resolve(filament_id_1based, num_physical, layer_index);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// Shortest hamilton path problem
|
||||
static std::vector<unsigned int> solve_extruder_order(const std::vector<std::vector<float>>& wipe_volumes, std::vector<unsigned int> all_extruders, std::optional<unsigned int> start_extruder_id)
|
||||
@@ -149,9 +184,13 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
|
||||
// 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 resolve_mixed_with_layer_heights(mixed_mgr,
|
||||
num_physical,
|
||||
filament_id,
|
||||
this->layer_index,
|
||||
mixed_layer_height_a,
|
||||
mixed_layer_height_b,
|
||||
mixed_base_layer_height);
|
||||
}
|
||||
|
||||
// Return a zero based extruder from the region, or extruder_override if overriden.
|
||||
@@ -223,6 +262,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
|
||||
// Mixed filament support.
|
||||
m_mixed_mgr = &object.print()->mixed_filament_manager();
|
||||
m_num_physical = object.print()->config().filament_diameter.size();
|
||||
update_mixed_layer_height_settings();
|
||||
if (object.layers().empty())
|
||||
return;
|
||||
|
||||
@@ -289,6 +329,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
||||
// Mixed filament support.
|
||||
m_mixed_mgr = &print.mixed_filament_manager();
|
||||
m_num_physical = print.config().filament_diameter.size();
|
||||
update_mixed_layer_height_settings();
|
||||
|
||||
// Initialize the print layers for all objects and all layers.
|
||||
coordf_t object_bottom_z = 0.;
|
||||
@@ -363,6 +404,33 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
||||
this->mark_skirt_layers(print.config(), max_layer_height);
|
||||
}
|
||||
|
||||
void ToolOrdering::update_mixed_layer_height_settings()
|
||||
{
|
||||
const PrintConfig *cfg = m_print_config_ptr;
|
||||
if (cfg == nullptr && m_print_object_ptr != nullptr)
|
||||
cfg = &m_print_object_ptr->print()->config();
|
||||
|
||||
m_mixed_layer_height_a = 0.f;
|
||||
m_mixed_layer_height_b = 0.f;
|
||||
if (m_print_full_config != nullptr &&
|
||||
m_print_full_config->has("mixed_color_layer_height_a") &&
|
||||
m_print_full_config->has("mixed_color_layer_height_b")) {
|
||||
m_mixed_layer_height_a = float(m_print_full_config->opt_float("mixed_color_layer_height_a"));
|
||||
m_mixed_layer_height_b = float(m_print_full_config->opt_float("mixed_color_layer_height_b"));
|
||||
} else if (cfg != nullptr) {
|
||||
m_mixed_layer_height_a = cfg->mixed_color_layer_height_a.value;
|
||||
m_mixed_layer_height_b = cfg->mixed_color_layer_height_b.value;
|
||||
}
|
||||
|
||||
float base_height = 0.2f;
|
||||
if (m_print_object_ptr != nullptr)
|
||||
base_height = float(m_print_object_ptr->config().layer_height.value);
|
||||
else if (m_print_full_config != nullptr && m_print_full_config->has("layer_height"))
|
||||
base_height = float(m_print_full_config->opt_float("layer_height"));
|
||||
|
||||
m_mixed_base_layer_height = std::max<float>(0.01f, base_height);
|
||||
}
|
||||
|
||||
static void apply_first_layer_order(const DynamicPrintConfig* config, std::vector<unsigned int>& tool_order) {
|
||||
const ConfigOptionInts* first_layer_print_sequence_op = config->option<ConfigOptionInts>("first_layer_print_sequence");
|
||||
if (first_layer_print_sequence_op) {
|
||||
@@ -492,6 +560,14 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
|
||||
// Collect extruders reuqired to print layers.
|
||||
void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> &per_layer_extruder_switches)
|
||||
{
|
||||
for (LayerTools &layer_tools : m_layer_tools) {
|
||||
layer_tools.mixed_mgr = m_mixed_mgr;
|
||||
layer_tools.num_physical = m_num_physical;
|
||||
layer_tools.mixed_layer_height_a = m_mixed_layer_height_a;
|
||||
layer_tools.mixed_layer_height_b = m_mixed_layer_height_b;
|
||||
layer_tools.mixed_base_layer_height = m_mixed_base_layer_height;
|
||||
}
|
||||
|
||||
// Collect the support extruders.
|
||||
for (auto support_layer : object.support_layers()) {
|
||||
LayerTools &layer_tools = this->tools_for_layer(support_layer->print_z);
|
||||
@@ -524,9 +600,7 @@ 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.mixed_mgr = m_mixed_mgr;
|
||||
layer_tools.num_physical = m_num_physical;
|
||||
layer_tools.layer_index = layerCount;
|
||||
|
||||
// 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)
|
||||
@@ -1441,9 +1515,13 @@ int WipingExtrusions::get_support_interface_extruder_overrides(const PrintObject
|
||||
// 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;
|
||||
return resolve_mixed_with_layer_heights(m_mixed_mgr,
|
||||
m_num_physical,
|
||||
filament_id_1based,
|
||||
layer_index,
|
||||
m_mixed_layer_height_a,
|
||||
m_mixed_layer_height_b,
|
||||
m_mixed_base_layer_height);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -144,6 +144,10 @@ public:
|
||||
// Mixed-filament resolution context (set by ToolOrdering during collect_extruders).
|
||||
const MixedFilamentManager *mixed_mgr = nullptr;
|
||||
size_t num_physical = 0;
|
||||
// Optional mixed-layer cadence override from print settings.
|
||||
float mixed_layer_height_a = 0.f;
|
||||
float mixed_layer_height_b = 0.f;
|
||||
float mixed_base_layer_height = 0.2f;
|
||||
|
||||
private:
|
||||
// Resolve a 1-based filament ID through the mixed-filament manager for this layer.
|
||||
@@ -211,6 +215,7 @@ private:
|
||||
// BBS
|
||||
std::vector<unsigned int> generate_first_layer_tool_order(const Print& print);
|
||||
std::vector<unsigned int> generate_first_layer_tool_order(const PrintObject& object);
|
||||
void update_mixed_layer_height_settings();
|
||||
|
||||
// Resolve a 1-based filament ID through the mixed-filament manager.
|
||||
// Returns the resolved physical extruder (1-based). If the ID is not a
|
||||
@@ -233,6 +238,9 @@ private:
|
||||
// number of physical extruders.
|
||||
const MixedFilamentManager* m_mixed_mgr = nullptr;
|
||||
size_t m_num_physical = 0;
|
||||
float m_mixed_layer_height_a = 0.f;
|
||||
float m_mixed_layer_height_b = 0.f;
|
||||
float m_mixed_base_layer_height = 0.2f;
|
||||
};
|
||||
|
||||
} // namespace SLic3r
|
||||
|
||||
@@ -79,6 +79,27 @@ void MixedFilamentManager::auto_generate(const std::vector<std::string> &filamen
|
||||
}
|
||||
}
|
||||
|
||||
void MixedFilamentManager::remove_physical_filament(unsigned int deleted_filament_id)
|
||||
{
|
||||
if (deleted_filament_id == 0 || m_mixed.empty())
|
||||
return;
|
||||
|
||||
std::vector<MixedFilament> filtered;
|
||||
filtered.reserve(m_mixed.size());
|
||||
for (MixedFilament mf : m_mixed) {
|
||||
if (mf.component_a == deleted_filament_id || mf.component_b == deleted_filament_id)
|
||||
continue;
|
||||
|
||||
if (mf.component_a > deleted_filament_id)
|
||||
--mf.component_a;
|
||||
if (mf.component_b > deleted_filament_id)
|
||||
--mf.component_b;
|
||||
|
||||
filtered.emplace_back(std::move(mf));
|
||||
}
|
||||
m_mixed = std::move(filtered);
|
||||
}
|
||||
|
||||
unsigned int MixedFilamentManager::resolve(unsigned int filament_id,
|
||||
size_t num_physical,
|
||||
int layer_index) const
|
||||
|
||||
@@ -61,6 +61,11 @@ public:
|
||||
// exists.
|
||||
void auto_generate(const std::vector<std::string> &filament_colours);
|
||||
|
||||
// Remove a physical filament (1-based ID) from the mixed list.
|
||||
// Any mixed filament that contains the removed component is deleted.
|
||||
// Remaining component IDs are shifted down to stay aligned with physical IDs.
|
||||
void remove_physical_filament(unsigned int deleted_filament_id);
|
||||
|
||||
// ---- Queries --------------------------------------------------------
|
||||
|
||||
// True when `filament_id` (1-based) refers to a mixed filament.
|
||||
|
||||
@@ -2507,6 +2507,20 @@ void ModelVolume::update_extruder_count_when_delete_filament(size_t extruder_cou
|
||||
}
|
||||
}
|
||||
|
||||
void ModelVolume::remap_extruder_ids(size_t extruder_count, const EnforcerBlockerStateMap &state_map)
|
||||
{
|
||||
std::vector<int> used_extruders = get_extruders();
|
||||
for (int extruder_id : used_extruders) {
|
||||
if (extruder_id <= 0)
|
||||
continue;
|
||||
if (extruder_id >= int(state_map.size()) || extruder_id > int(extruder_count) ||
|
||||
state_map[size_t(extruder_id)] != EnforcerBlockerType(extruder_id)) {
|
||||
mmu_segmentation_facets.remap_enforcer_block_types(*this, EnforcerBlockerType(extruder_count), state_map);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelVolume::center_geometry_after_creation(bool update_source_offset)
|
||||
{
|
||||
Vec3d shift = this->mesh().bounding_box().center();
|
||||
@@ -3411,6 +3425,15 @@ void FacetsAnnotation::set_enforcer_block_type_limit(const ModelVolume& mv,
|
||||
this->set(selector);
|
||||
}
|
||||
|
||||
void FacetsAnnotation::remap_enforcer_block_types(const ModelVolume& mv,
|
||||
EnforcerBlockerType max_type,
|
||||
const EnforcerBlockerStateMap &state_map)
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
selector.deserialize(m_data, false, max_type, EnforcerBlockerType::NONE, EnforcerBlockerType::NONE, &state_map);
|
||||
this->set(selector);
|
||||
}
|
||||
|
||||
indexed_triangle_set FacetsAnnotation::get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
|
||||
@@ -738,6 +738,9 @@ public:
|
||||
EnforcerBlockerType max_type,
|
||||
EnforcerBlockerType to_delete_filament = EnforcerBlockerType::NONE,
|
||||
EnforcerBlockerType replace_filament = EnforcerBlockerType::NONE);
|
||||
void remap_enforcer_block_types(const ModelVolume& mv,
|
||||
EnforcerBlockerType max_type,
|
||||
const EnforcerBlockerStateMap &state_map);
|
||||
indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
bool empty() const { return m_data.triangles_to_split.empty(); }
|
||||
@@ -919,6 +922,7 @@ public:
|
||||
std::vector<int> get_extruders() const;
|
||||
void update_extruder_count(size_t extruder_count);
|
||||
void update_extruder_count_when_delete_filament(size_t extruder_count, size_t filament_id, int replace_filament_id = -1);
|
||||
void remap_extruder_ids(size_t extruder_count, const EnforcerBlockerStateMap &state_map);
|
||||
|
||||
// Split this volume, append the result to the object owning this volume.
|
||||
// Return the number of volumes created from this one.
|
||||
|
||||
@@ -3231,6 +3231,10 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
||||
#else
|
||||
size_t num_filaments = this->filament_presets.size();
|
||||
#endif
|
||||
const bool deleting_filament = (to_delete_filament_id != size_t(-1));
|
||||
const size_t old_num_filaments = deleting_filament ? (num_filaments + 1) : num_filaments;
|
||||
const std::vector<MixedFilament> old_mixed = this->mixed_filaments.mixed_filaments();
|
||||
m_last_filament_id_remap.clear();
|
||||
|
||||
// Now verify if flush_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator):
|
||||
std::vector<double> old_matrix = this->project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values;
|
||||
@@ -3259,6 +3263,12 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
||||
this->project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values = new_matrix;
|
||||
}
|
||||
|
||||
// Keep mixed (virtual) combinations in sync with physical filament deletion.
|
||||
// Mixed entries containing the deleted physical filament are removed, while
|
||||
// remaining component IDs are shifted.
|
||||
if (deleting_filament)
|
||||
this->mixed_filaments.remove_physical_filament(unsigned(to_delete_filament_id + 1));
|
||||
|
||||
// Keep project colours aligned to physical filaments, then regenerate mixed
|
||||
// (virtual) entries from the physical set only.
|
||||
{
|
||||
@@ -3268,6 +3278,54 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
||||
this->mixed_filaments.auto_generate(color_opt->values);
|
||||
}
|
||||
}
|
||||
|
||||
// Build old->new filament ID remap for painted facet data normalization.
|
||||
if (deleting_filament) {
|
||||
const unsigned int deleted_1based = unsigned(to_delete_filament_id + 1);
|
||||
|
||||
size_t old_enabled_mixed = 0;
|
||||
for (const auto &mf : old_mixed)
|
||||
if (mf.enabled)
|
||||
++old_enabled_mixed;
|
||||
|
||||
const size_t old_total_filaments = old_num_filaments + old_enabled_mixed;
|
||||
m_last_filament_id_remap.assign(old_total_filaments + 1, 0);
|
||||
|
||||
for (unsigned int old_id = 1; old_id <= unsigned(old_num_filaments); ++old_id) {
|
||||
if (old_id == deleted_1based)
|
||||
m_last_filament_id_remap[old_id] = 0;
|
||||
else
|
||||
m_last_filament_id_remap[old_id] = old_id > deleted_1based ? old_id - 1 : old_id;
|
||||
}
|
||||
|
||||
std::map<std::pair<unsigned int, unsigned int>, unsigned int> new_pair_to_id;
|
||||
unsigned int next_virtual_id = unsigned(num_filaments + 1);
|
||||
for (const auto &mf : this->mixed_filaments.mixed_filaments()) {
|
||||
if (!mf.enabled)
|
||||
continue;
|
||||
new_pair_to_id[{mf.component_a, mf.component_b}] = next_virtual_id++;
|
||||
}
|
||||
|
||||
unsigned int old_virtual_id = unsigned(old_num_filaments + 1);
|
||||
for (const auto &mf : old_mixed) {
|
||||
if (!mf.enabled)
|
||||
continue;
|
||||
|
||||
unsigned int a = mf.component_a;
|
||||
unsigned int b = mf.component_b;
|
||||
if (a == deleted_1based || b == deleted_1based) {
|
||||
m_last_filament_id_remap[old_virtual_id] = 0;
|
||||
} else {
|
||||
if (a > deleted_1based)
|
||||
--a;
|
||||
if (b > deleted_1based)
|
||||
--b;
|
||||
auto it = new_pair_to_id.find({a, b});
|
||||
m_last_filament_id_remap[old_virtual_id] = (it == new_pair_to_id.end()) ? 0 : it->second;
|
||||
}
|
||||
++old_virtual_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible)
|
||||
|
||||
@@ -253,6 +253,9 @@ public:
|
||||
// Read out the number of extruders from an active printer preset,
|
||||
// update size and content of filament_presets.
|
||||
void update_multi_material_filament_presets(size_t to_delete_filament_id = size_t(-1));
|
||||
// Mapping generated during the latest filament deletion.
|
||||
// Index is old 1-based filament ID, value is new 1-based filament ID (0 = removed).
|
||||
const std::vector<unsigned int>& last_filament_id_remap() const { return m_last_filament_id_remap; }
|
||||
|
||||
// Update the is_compatible flag of all print and filament presets depending on whether they are marked
|
||||
// as compatible with the currently selected printer (and print in case of filament presets).
|
||||
@@ -324,6 +327,7 @@ private:
|
||||
bool validation_mode = false;
|
||||
std::string vendor_to_validate = "";
|
||||
int m_errors = 0;
|
||||
std::vector<unsigned int> m_last_filament_id_remap;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -245,6 +245,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "filament_shrinkage_compensation_z"
|
||||
|| opt_key == "resolution"
|
||||
|| opt_key == "precise_z_height"
|
||||
|| opt_key == "dithering_z_step_size"
|
||||
|| opt_key == "dithering_step_painted_zones_only"
|
||||
// Spiral Vase forces different kind of slicing than the normal model:
|
||||
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
|
||||
// Therefore toggling the Spiral Vase on / off requires complete reslicing.
|
||||
@@ -1130,7 +1132,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
|
||||
std::vector<coordf_t> &profile = layer_height_profiles[print_object_idx];
|
||||
if (profile.empty())
|
||||
PrintObject::update_layer_height_profile(*print_object.model_object(), print_object.slicing_parameters(), profile);
|
||||
PrintObject::update_layer_height_profile(*print_object.model_object(), print_object.slicing_parameters(), profile, &print_object);
|
||||
return profile;
|
||||
};
|
||||
|
||||
|
||||
@@ -410,7 +410,10 @@ public:
|
||||
|
||||
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
|
||||
// Returns true, if the layer_height_profile was changed.
|
||||
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
|
||||
static bool update_layer_height_profile(const ModelObject &model_object,
|
||||
const SlicingParameters &slicing_parameters,
|
||||
std::vector<coordf_t> &layer_height_profile,
|
||||
const PrintObject *print_object = nullptr);
|
||||
|
||||
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
||||
// by the interactive layer height editor and by the printing process itself.
|
||||
|
||||
@@ -1092,6 +1092,13 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
new_full_config.option("print_settings_id", true);
|
||||
new_full_config.option("filament_settings_id", true);
|
||||
new_full_config.option("printer_settings_id", true);
|
||||
// Ensure newly introduced dithering keys are present so in-session updates are detected.
|
||||
new_full_config.option("dithering_z_step_size", true);
|
||||
new_full_config.option("dithering_step_painted_zones_only", true);
|
||||
m_config.option("dithering_z_step_size", true);
|
||||
m_config.option("dithering_step_painted_zones_only", true);
|
||||
m_default_object_config.option("dithering_z_step_size", true);
|
||||
m_default_object_config.option("dithering_step_painted_zones_only", true);
|
||||
// BBS
|
||||
int used_filaments = this->extruders(true).size();
|
||||
|
||||
|
||||
@@ -4121,6 +4121,44 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionStrings());
|
||||
|
||||
def = this->add("mixed_color_layer_height_a", coFloat);
|
||||
def->label = L("Dithering cadence height A");
|
||||
def->category = L("Others");
|
||||
def->tooltip = L("Layer height contribution of component A for dithering virtual filaments. "
|
||||
"Set to 0 to use normal 1-layer A / 1-layer B alternation.");
|
||||
def->sidetext = "mm";
|
||||
def->min = 0.;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.0));
|
||||
|
||||
def = this->add("mixed_color_layer_height_b", coFloat);
|
||||
def->label = L("Dithering cadence height B");
|
||||
def->category = L("Others");
|
||||
def->tooltip = L("Layer height contribution of component B for dithering virtual filaments. "
|
||||
"Set to 0 to use normal 1-layer A / 1-layer B alternation.");
|
||||
def->sidetext = "mm";
|
||||
def->min = 0.;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.0));
|
||||
|
||||
def = this->add("dithering_z_step_size", coFloat);
|
||||
def->label = L("Dithering Z step size");
|
||||
def->category = L("Others");
|
||||
def->tooltip = L("Layer height used in Z zones painted with dithering (mixed virtual filaments). "
|
||||
"Set to 0 to keep normal layer height in those zones.");
|
||||
def->sidetext = "mm";
|
||||
def->min = 0.;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.0));
|
||||
|
||||
def = this->add("dithering_step_painted_zones_only", coBool);
|
||||
def->label = L("Use step size in painted zones only");
|
||||
def->category = L("Others");
|
||||
def->tooltip = L("When enabled, dithering Z step size is applied only where mixed filament is painted. "
|
||||
"Unpainted zones keep their original layer height.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("printer_model", coString);
|
||||
def->label = L("Printer type");
|
||||
def->tooltip = L("Type of the printer.");
|
||||
|
||||
@@ -1351,6 +1351,10 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionBool, ooze_prevention))
|
||||
((ConfigOptionString, filename_format))
|
||||
((ConfigOptionStrings, post_process))
|
||||
((ConfigOptionFloat, mixed_color_layer_height_a))
|
||||
((ConfigOptionFloat, mixed_color_layer_height_b))
|
||||
((ConfigOptionFloat, dithering_z_step_size))
|
||||
((ConfigOptionBool, dithering_step_painted_zones_only))
|
||||
((ConfigOptionString, printer_model))
|
||||
((ConfigOptionFloat, resolution))
|
||||
((ConfigOptionFloats, retraction_minimum_travel))
|
||||
|
||||
@@ -946,6 +946,8 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
steps.emplace_back(posPerimeters);
|
||||
} else if (
|
||||
opt_key == "layer_height"
|
||||
|| opt_key == "dithering_z_step_size"
|
||||
|| opt_key == "dithering_step_painted_zones_only"
|
||||
|| opt_key == "mmu_segmented_region_max_width"
|
||||
|| opt_key == "mmu_segmented_region_interlocking_depth"
|
||||
|| opt_key == "raft_layers"
|
||||
@@ -3331,10 +3333,263 @@ std::vector<unsigned int> PrintObject::object_extruders() const
|
||||
return extruders;
|
||||
}
|
||||
|
||||
bool PrintObject::update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile)
|
||||
namespace {
|
||||
|
||||
struct LayerHeightRangeOverride {
|
||||
coordf_t lo { 0.f };
|
||||
coordf_t hi { 0.f };
|
||||
coordf_t height { 0.f };
|
||||
};
|
||||
|
||||
static void sort_and_merge_layer_ranges(std::vector<t_layer_height_range> &ranges)
|
||||
{
|
||||
if (ranges.empty())
|
||||
return;
|
||||
|
||||
std::sort(ranges.begin(), ranges.end(), [](const t_layer_height_range &a, const t_layer_height_range &b) {
|
||||
return a.first < b.first || (a.first == b.first && a.second < b.second);
|
||||
});
|
||||
|
||||
std::vector<t_layer_height_range> merged;
|
||||
merged.reserve(ranges.size());
|
||||
for (const t_layer_height_range &range : ranges) {
|
||||
if (range.second <= range.first + EPSILON)
|
||||
continue;
|
||||
|
||||
if (merged.empty() || range.first > merged.back().second + EPSILON) {
|
||||
merged.emplace_back(range);
|
||||
} else {
|
||||
merged.back().second = std::max(merged.back().second, range.second);
|
||||
}
|
||||
}
|
||||
ranges = std::move(merged);
|
||||
}
|
||||
|
||||
static std::vector<t_layer_height_range> collect_mixed_painted_z_ranges(const PrintObject &print_object, coordf_t object_height)
|
||||
{
|
||||
std::vector<t_layer_height_range> mixed_ranges;
|
||||
if (object_height <= EPSILON)
|
||||
return mixed_ranges;
|
||||
|
||||
const Print *print = print_object.print();
|
||||
if (print == nullptr)
|
||||
return mixed_ranges;
|
||||
|
||||
const size_t num_physical = print->config().filament_colour.size();
|
||||
const size_t num_total = print->mixed_filament_manager().total_filaments(num_physical);
|
||||
if (num_total <= num_physical)
|
||||
return mixed_ranges;
|
||||
|
||||
const size_t max_state = std::min(num_total, size_t(EnforcerBlockerType::ExtruderMax));
|
||||
const Transform3d object_to_print = print_object.trafo_centered();
|
||||
|
||||
for (const ModelVolume *mv : print_object.model_object()->volumes) {
|
||||
if (mv == nullptr || !mv->is_model_part() || mv->mmu_segmentation_facets.empty())
|
||||
continue;
|
||||
|
||||
const auto &used_states = mv->mmu_segmentation_facets.get_data().used_states;
|
||||
if (used_states.empty())
|
||||
continue;
|
||||
|
||||
const Transform3d volume_to_print = object_to_print * mv->get_matrix();
|
||||
constexpr coordf_t thin_band = 0.01f;
|
||||
for (size_t state_idx = num_physical + 1; state_idx <= max_state; ++state_idx) {
|
||||
if (state_idx >= used_states.size() || !used_states[state_idx])
|
||||
continue;
|
||||
|
||||
const auto facets = mv->mmu_segmentation_facets.get_facets_strict(*mv, static_cast<EnforcerBlockerType>(state_idx));
|
||||
if (facets.indices.empty() || facets.vertices.empty())
|
||||
continue;
|
||||
|
||||
for (const auto &face : facets.indices) {
|
||||
double tri_z_min = DBL_MAX;
|
||||
double tri_z_max = -DBL_MAX;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const size_t vertex_idx = size_t(face[i]);
|
||||
if (vertex_idx >= facets.vertices.size())
|
||||
continue;
|
||||
const Vec3d p = volume_to_print * facets.vertices[vertex_idx].cast<double>();
|
||||
tri_z_min = std::min(tri_z_min, p.z());
|
||||
tri_z_max = std::max(tri_z_max, p.z());
|
||||
}
|
||||
|
||||
if (tri_z_min == DBL_MAX || tri_z_max == -DBL_MAX)
|
||||
continue;
|
||||
|
||||
coordf_t lo = std::max<coordf_t>(0.f, coordf_t(tri_z_min));
|
||||
coordf_t hi = std::min<coordf_t>(object_height, coordf_t(tri_z_max));
|
||||
if (hi <= lo + EPSILON) {
|
||||
const coordf_t center = std::max<coordf_t>(0.f, std::min<coordf_t>(object_height, lo));
|
||||
lo = std::max<coordf_t>(0.f, center - thin_band * 0.5f);
|
||||
hi = std::min<coordf_t>(object_height, center + thin_band * 0.5f);
|
||||
}
|
||||
if (lo + EPSILON < hi)
|
||||
mixed_ranges.emplace_back(lo, hi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort_and_merge_layer_ranges(mixed_ranges);
|
||||
return mixed_ranges;
|
||||
}
|
||||
|
||||
static std::vector<LayerHeightRangeOverride> base_layer_height_overrides(const t_layer_config_ranges &ranges, coordf_t object_height)
|
||||
{
|
||||
std::vector<LayerHeightRangeOverride> out;
|
||||
out.reserve(ranges.size());
|
||||
|
||||
coordf_t last_hi = 0.f;
|
||||
for (const auto &[range, config] : ranges) {
|
||||
coordf_t lo = std::max(range.first, last_hi);
|
||||
coordf_t hi = std::min(range.second, object_height);
|
||||
if (lo + EPSILON >= hi)
|
||||
continue;
|
||||
|
||||
const ConfigOption *layer_height_opt = config.option("layer_height");
|
||||
if (layer_height_opt == nullptr)
|
||||
continue;
|
||||
|
||||
out.push_back({ lo, hi, coordf_t(layer_height_opt->getFloat()) });
|
||||
last_hi = hi;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool contains_z(const std::vector<t_layer_height_range> &ranges, coordf_t z)
|
||||
{
|
||||
for (const t_layer_height_range &range : ranges) {
|
||||
if (z + EPSILON < range.first)
|
||||
break;
|
||||
if (z + EPSILON >= range.first && z < range.second - EPSILON)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool get_override_height(const std::vector<LayerHeightRangeOverride> &ranges, coordf_t z, coordf_t &height_out)
|
||||
{
|
||||
for (const LayerHeightRangeOverride &range : ranges) {
|
||||
if (z + EPSILON < range.lo)
|
||||
break;
|
||||
if (z + EPSILON >= range.lo && z < range.hi - EPSILON) {
|
||||
height_out = range.height;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static t_layer_config_ranges layer_ranges_with_dithering(const t_layer_config_ranges &base_ranges_map,
|
||||
coordf_t object_height,
|
||||
coordf_t default_layer_height,
|
||||
const std::vector<t_layer_height_range> &mixed_ranges,
|
||||
coordf_t dithering_step)
|
||||
{
|
||||
if (object_height <= EPSILON || mixed_ranges.empty() || dithering_step <= EPSILON)
|
||||
return base_ranges_map;
|
||||
|
||||
const std::vector<LayerHeightRangeOverride> base_ranges = base_layer_height_overrides(base_ranges_map, object_height);
|
||||
|
||||
std::vector<coordf_t> boundaries;
|
||||
boundaries.reserve(2 + base_ranges.size() * 2 + mixed_ranges.size() * 2);
|
||||
boundaries.emplace_back(0.f);
|
||||
boundaries.emplace_back(object_height);
|
||||
for (const LayerHeightRangeOverride &range : base_ranges) {
|
||||
boundaries.emplace_back(range.lo);
|
||||
boundaries.emplace_back(range.hi);
|
||||
}
|
||||
for (const t_layer_height_range &range : mixed_ranges) {
|
||||
boundaries.emplace_back(range.first);
|
||||
boundaries.emplace_back(range.second);
|
||||
}
|
||||
|
||||
std::sort(boundaries.begin(), boundaries.end());
|
||||
boundaries.erase(std::unique(boundaries.begin(), boundaries.end(), [](coordf_t a, coordf_t b) {
|
||||
return std::abs(a - b) <= EPSILON;
|
||||
}),
|
||||
boundaries.end());
|
||||
|
||||
std::vector<LayerHeightRangeOverride> merged_ranges;
|
||||
merged_ranges.reserve(boundaries.size());
|
||||
for (size_t i = 1; i < boundaries.size(); ++i) {
|
||||
const coordf_t lo = boundaries[i - 1];
|
||||
const coordf_t hi = boundaries[i];
|
||||
if (hi <= lo + EPSILON)
|
||||
continue;
|
||||
|
||||
const coordf_t z_mid = 0.5f * (lo + hi);
|
||||
|
||||
coordf_t target_height = default_layer_height;
|
||||
coordf_t base_height = 0.f;
|
||||
if (contains_z(mixed_ranges, z_mid)) {
|
||||
target_height = dithering_step;
|
||||
} else if (get_override_height(base_ranges, z_mid, base_height)) {
|
||||
target_height = base_height;
|
||||
}
|
||||
|
||||
if (std::abs(target_height - default_layer_height) <= EPSILON)
|
||||
continue;
|
||||
|
||||
if (!merged_ranges.empty() &&
|
||||
std::abs(merged_ranges.back().height - target_height) <= EPSILON &&
|
||||
std::abs(merged_ranges.back().hi - lo) <= EPSILON) {
|
||||
merged_ranges.back().hi = hi;
|
||||
} else {
|
||||
merged_ranges.push_back({ lo, hi, target_height });
|
||||
}
|
||||
}
|
||||
|
||||
t_layer_config_ranges out;
|
||||
for (const LayerHeightRangeOverride &range : merged_ranges) {
|
||||
if (range.hi <= range.lo + EPSILON)
|
||||
continue;
|
||||
ModelConfig cfg;
|
||||
cfg.set_key_value("layer_height", new ConfigOptionFloat(range.height));
|
||||
out.emplace(t_layer_height_range(range.lo, range.hi), std::move(cfg));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool PrintObject::update_layer_height_profile(const ModelObject &model_object,
|
||||
const SlicingParameters &slicing_parameters,
|
||||
std::vector<coordf_t> &layer_height_profile,
|
||||
const PrintObject *print_object)
|
||||
{
|
||||
bool updated = false;
|
||||
|
||||
const t_layer_config_ranges *ranges_to_use = &model_object.layer_config_ranges;
|
||||
t_layer_config_ranges dithering_ranges;
|
||||
if (print_object != nullptr && print_object->print() != nullptr) {
|
||||
const DynamicPrintConfig &full_cfg = print_object->print()->full_print_config();
|
||||
coordf_t dithering_step = coordf_t(print_object->print()->config().dithering_z_step_size.value);
|
||||
bool painted_zones_only = print_object->print()->config().dithering_step_painted_zones_only.value;
|
||||
if (full_cfg.has("dithering_z_step_size"))
|
||||
dithering_step = coordf_t(full_cfg.opt_float("dithering_z_step_size"));
|
||||
if (full_cfg.has("dithering_step_painted_zones_only"))
|
||||
painted_zones_only = full_cfg.opt_bool("dithering_step_painted_zones_only");
|
||||
|
||||
if (dithering_step > EPSILON) {
|
||||
const coordf_t object_height = slicing_parameters.object_print_z_uncompensated_height();
|
||||
std::vector<t_layer_height_range> mixed_ranges;
|
||||
if (painted_zones_only)
|
||||
mixed_ranges = collect_mixed_painted_z_ranges(*print_object, object_height);
|
||||
else if (object_height > EPSILON)
|
||||
mixed_ranges.emplace_back(0.f, object_height);
|
||||
|
||||
if (!mixed_ranges.empty()) {
|
||||
dithering_ranges = layer_ranges_with_dithering(model_object.layer_config_ranges,
|
||||
object_height,
|
||||
slicing_parameters.layer_height,
|
||||
mixed_ranges,
|
||||
dithering_step);
|
||||
ranges_to_use = &dithering_ranges;
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool has_dithering_ranges = (ranges_to_use != &model_object.layer_config_ranges);
|
||||
|
||||
if (layer_height_profile.empty()) {
|
||||
// use the constructor because the assignement is crashing on ASAN OsX
|
||||
layer_height_profile = std::vector<coordf_t>(model_object.layer_height_profile.get());
|
||||
@@ -3352,9 +3607,9 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_uncompensated_max + slicing_parameters.object_print_z_min) > 1e-3))
|
||||
layer_height_profile.clear();
|
||||
|
||||
if (layer_height_profile.empty() || layer_height_profile[1] != slicing_parameters.first_object_layer_height) {
|
||||
if (layer_height_profile.empty() || layer_height_profile[1] != slicing_parameters.first_object_layer_height || has_dithering_ranges) {
|
||||
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
||||
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);
|
||||
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, *ranges_to_use);
|
||||
// The layer height profile is already compressed.
|
||||
updated = true;
|
||||
}
|
||||
|
||||
@@ -793,7 +793,7 @@ void PrintObject::slice()
|
||||
//BBS: add flag to reload scene for shell rendering
|
||||
m_print->set_status(5, L("Slicing mesh"), PrintBase::SlicingStatus::RELOAD_SCENE);
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile);
|
||||
this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile, this);
|
||||
m_print->throw_if_canceled();
|
||||
m_typed_slices = false;
|
||||
this->clear_layers();
|
||||
|
||||
@@ -1724,7 +1724,8 @@ void TriangleSelector::deserialize(const TriangleSplittingData& data,
|
||||
bool needs_reset,
|
||||
EnforcerBlockerType max_ebt,
|
||||
EnforcerBlockerType to_delete_filament,
|
||||
EnforcerBlockerType replace_filament)
|
||||
EnforcerBlockerType replace_filament,
|
||||
const EnforcerBlockerStateMap* state_map)
|
||||
{
|
||||
if (needs_reset)
|
||||
reset(); // dump any current state
|
||||
@@ -1790,11 +1791,15 @@ void TriangleSelector::deserialize(const TriangleSplittingData& data,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (state == to_delete_filament)
|
||||
state = replace_filament;
|
||||
else if (to_delete_filament != EnforcerBlockerType::NONE && state != EnforcerBlockerType::NONE) {
|
||||
state = state > to_delete_filament ? EnforcerBlockerType((int) state - 1) : state;
|
||||
if (state_map != nullptr && state != EnforcerBlockerType::NONE) {
|
||||
const size_t state_idx = static_cast<size_t>(state);
|
||||
state = state_idx < state_map->size() ? (*state_map)[state_idx] : EnforcerBlockerType::NONE;
|
||||
} else {
|
||||
if (state == to_delete_filament)
|
||||
state = replace_filament;
|
||||
else if (to_delete_filament != EnforcerBlockerType::NONE && state != EnforcerBlockerType::NONE) {
|
||||
state = state > to_delete_filament ? EnforcerBlockerType((int) state - 1) : state;
|
||||
}
|
||||
}
|
||||
|
||||
if (state > max_ebt) {
|
||||
|
||||
@@ -360,7 +360,8 @@ public:
|
||||
bool needs_reset = true,
|
||||
EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax,
|
||||
EnforcerBlockerType to_delete_filament = EnforcerBlockerType::NONE,
|
||||
EnforcerBlockerType replace_filament = EnforcerBlockerType::NONE);
|
||||
EnforcerBlockerType replace_filament = EnforcerBlockerType::NONE,
|
||||
const EnforcerBlockerStateMap *state_map = nullptr);
|
||||
|
||||
// Extract all used facet states from the given TriangleSplittingData.
|
||||
static std::vector<EnforcerBlockerType> extract_used_facet_states(const TriangleSplittingData &data);
|
||||
|
||||
+10
-2
@@ -108,13 +108,21 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
|
||||
{
|
||||
try{
|
||||
|
||||
if (config.def()->get(opt_key)->type == coBools && config.def()->get(opt_key)->nullable) {
|
||||
const ConfigOptionDef *opt_def = config.def()->get(opt_key);
|
||||
if (opt_def == nullptr)
|
||||
throw Slic3r::RuntimeError("Unknown config option key: " + opt_key);
|
||||
|
||||
// Some older presets may not carry newly introduced keys. Ensure the
|
||||
// option exists before mutating it through typed accessors below.
|
||||
if (!config.has(opt_key))
|
||||
config.set_key_value(opt_key, opt_def->create_default_option());
|
||||
|
||||
if (opt_def->type == coBools && opt_def->nullable) {
|
||||
ConfigOptionBoolsNullable* vec_new = new ConfigOptionBoolsNullable{ boost::any_cast<unsigned char>(value) };
|
||||
config.option<ConfigOptionBoolsNullable>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const ConfigOptionDef *opt_def = config.def()->get(opt_key);
|
||||
switch (opt_def->type) {
|
||||
case coFloatOrPercent:{
|
||||
std::string str = boost::any_cast<std::string>(value);
|
||||
|
||||
@@ -981,10 +981,29 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
||||
case coPercents:
|
||||
case coFloats:
|
||||
case coFloat:{
|
||||
double val = opt->type == coFloats ?
|
||||
double val = 0.0;
|
||||
if (config.has(opt_key)) {
|
||||
val = opt->type == coFloats ?
|
||||
config.opt_float(opt_key, idx) :
|
||||
opt->type == coFloat ? config.opt_float(opt_key) :
|
||||
config.option<ConfigOptionPercents>(opt_key)->get_at(idx);
|
||||
} else if (opt->default_value) {
|
||||
if (opt->type == coFloats) {
|
||||
const auto *defaults = dynamic_cast<const ConfigOptionFloats*>(opt->default_value.get());
|
||||
if (defaults != nullptr && !defaults->values.empty()) {
|
||||
const size_t safe_idx = std::min(idx, defaults->values.size() - 1);
|
||||
val = defaults->values[safe_idx];
|
||||
}
|
||||
} else if (opt->type == coFloat) {
|
||||
val = opt->default_value->getFloat();
|
||||
} else {
|
||||
const auto *defaults = dynamic_cast<const ConfigOptionPercents*>(opt->default_value.get());
|
||||
if (defaults != nullptr && !defaults->values.empty()) {
|
||||
const size_t safe_idx = std::min(idx, defaults->values.size() - 1);
|
||||
val = defaults->values[safe_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = double_to_string(val);
|
||||
}
|
||||
break;
|
||||
@@ -1009,10 +1028,29 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
||||
ret = from_u8(config.opt_string(opt_key, static_cast<unsigned int>(idx)));
|
||||
break;
|
||||
case coBool:
|
||||
ret = config.opt_bool(opt_key);
|
||||
if (config.has(opt_key)) {
|
||||
ret = config.opt_bool(opt_key);
|
||||
} else if (opt->default_value) {
|
||||
const auto *defaults = dynamic_cast<const ConfigOptionBool*>(opt->default_value.get());
|
||||
ret = defaults != nullptr ? defaults->value : false;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
break;
|
||||
case coBools:
|
||||
ret = config.opt_bool(opt_key, idx);
|
||||
if (config.has(opt_key)) {
|
||||
ret = config.opt_bool(opt_key, idx);
|
||||
} else if (opt->default_value) {
|
||||
const auto *defaults = dynamic_cast<const ConfigOptionBools*>(opt->default_value.get());
|
||||
if (defaults != nullptr && !defaults->values.empty()) {
|
||||
const size_t safe_idx = std::min(idx, defaults->values.size() - 1);
|
||||
ret = defaults->values[safe_idx];
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
break;
|
||||
case coInt:
|
||||
ret = config.opt_int(opt_key);
|
||||
@@ -1139,10 +1177,27 @@ boost::any ConfigOptionsGroup::get_config_value2(const DynamicPrintConfig& confi
|
||||
ret = config.opt_string(opt_key, static_cast<unsigned int>(idx));
|
||||
break;
|
||||
case coBool:
|
||||
ret = config.opt_bool(opt_key);
|
||||
if (config.has(opt_key))
|
||||
ret = config.opt_bool(opt_key);
|
||||
else if (opt->default_value) {
|
||||
const auto *defaults = dynamic_cast<const ConfigOptionBool*>(opt->default_value.get());
|
||||
ret = defaults != nullptr ? defaults->value : false;
|
||||
} else
|
||||
ret = false;
|
||||
break;
|
||||
case coBools:
|
||||
ret = static_cast<unsigned char>(config.opt_bool(opt_key, idx));
|
||||
if (config.has(opt_key))
|
||||
ret = static_cast<unsigned char>(config.opt_bool(opt_key, idx));
|
||||
else if (opt->default_value) {
|
||||
const auto *defaults = dynamic_cast<const ConfigOptionBools*>(opt->default_value.get());
|
||||
if (defaults != nullptr && !defaults->values.empty()) {
|
||||
const size_t safe_idx = std::min(idx, defaults->values.size() - 1);
|
||||
ret = static_cast<unsigned char>(defaults->values[safe_idx]);
|
||||
} else {
|
||||
ret = static_cast<unsigned char>(false);
|
||||
}
|
||||
} else
|
||||
ret = static_cast<unsigned char>(false);
|
||||
break;
|
||||
case coInt:
|
||||
ret = config.opt_int(opt_key);
|
||||
|
||||
@@ -1436,7 +1436,7 @@ Sidebar::Sidebar(Plater *parent)
|
||||
p->m_sizer_mixed_filaments = new wxBoxSizer(wxVERTICAL);
|
||||
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4));
|
||||
// Title
|
||||
auto *mixed_title = new wxStaticText(p->m_panel_mixed_filaments, wxID_ANY, _L("Mixed Colors"));
|
||||
auto *mixed_title = new wxStaticText(p->m_panel_mixed_filaments, wxID_ANY, _L("Dithering"));
|
||||
mixed_title->SetFont(Label::Head_14);
|
||||
p->m_sizer_mixed_filaments->Add(mixed_title, 0, wxLEFT | wxRIGHT, FromDIP(16));
|
||||
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4));
|
||||
@@ -2205,7 +2205,7 @@ void Sidebar::update_mixed_filament_panel()
|
||||
|
||||
// Recreate header.
|
||||
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4));
|
||||
auto *mixed_title = new wxStaticText(p->m_panel_mixed_filaments, wxID_ANY, _L("Mixed Colors"));
|
||||
auto *mixed_title = new wxStaticText(p->m_panel_mixed_filaments, wxID_ANY, _L("Dithering"));
|
||||
mixed_title->SetFont(Label::Head_14);
|
||||
p->m_sizer_mixed_filaments->Add(mixed_title, 0, wxLEFT | wxRIGHT, FromDIP(16));
|
||||
p->m_sizer_mixed_filaments->AddSpacer(FromDIP(4));
|
||||
@@ -2355,6 +2355,7 @@ void Sidebar::on_filaments_delete(size_t filament_id)
|
||||
p->m_panel_filament_title->Refresh();
|
||||
update_ui_from_settings();
|
||||
dynamic_filament_list.update();
|
||||
update_mixed_filament_panel();
|
||||
}
|
||||
|
||||
void Sidebar::edit_filament() {
|
||||
@@ -14317,10 +14318,40 @@ void Plater::on_filaments_delete(size_t num_filaments, size_t filament_id, int r
|
||||
}*/
|
||||
|
||||
// update mmu info
|
||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||
|
||||
size_t total_filaments = num_filaments;
|
||||
if (preset_bundle != nullptr) {
|
||||
const size_t current_num_physical = preset_bundle->filament_presets.size();
|
||||
total_filaments = preset_bundle->mixed_filaments.total_filaments(current_num_physical);
|
||||
}
|
||||
|
||||
EnforcerBlockerStateMap state_map;
|
||||
for (size_t i = 0; i < state_map.size(); ++i)
|
||||
state_map[i] = EnforcerBlockerType(i);
|
||||
|
||||
const bool can_use_remap = replace_filament_id < 0;
|
||||
static const std::vector<unsigned int> empty_remap;
|
||||
const std::vector<unsigned int> &id_remap =
|
||||
preset_bundle != nullptr ? preset_bundle->last_filament_id_remap() : empty_remap;
|
||||
if (can_use_remap && !id_remap.empty()) {
|
||||
for (size_t i = 1; i < state_map.size(); ++i) {
|
||||
const unsigned int mapped = i < id_remap.size() ? id_remap[i] : 0;
|
||||
if (mapped == 0 || mapped >= state_map.size() || mapped > total_filaments)
|
||||
state_map[i] = EnforcerBlockerType::NONE;
|
||||
else
|
||||
state_map[i] = EnforcerBlockerType(mapped);
|
||||
}
|
||||
}
|
||||
|
||||
for (ModelObject* mo : wxGetApp().model().objects) {
|
||||
for (ModelVolume* mv : mo->volumes) {
|
||||
mv->update_extruder_count_when_delete_filament(num_filaments, filament_id + 1,
|
||||
replace_filament_id + 1); // this function is 1 base
|
||||
if (can_use_remap && !id_remap.empty()) {
|
||||
mv->remap_extruder_ids(total_filaments, state_map);
|
||||
} else {
|
||||
mv->update_extruder_count_when_delete_filament(total_filaments, filament_id + 1,
|
||||
replace_filament_id + 1); // this function is 1 base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2488,6 +2488,13 @@ optgroup->append_single_option_line("skirt_loops", "others_settings_skirt#loops"
|
||||
|
||||
optgroup->append_single_option_line("timelapse_type", "others_settings_special_mode#timelapse");
|
||||
|
||||
// Use default (no icon) here to avoid runtime bitmap load failures.
|
||||
optgroup = page->new_optgroup(L("Dithering"));
|
||||
optgroup->append_single_option_line("mixed_color_layer_height_a", "others_settings_mixed_colors#layer-height-a");
|
||||
optgroup->append_single_option_line("mixed_color_layer_height_b", "others_settings_mixed_colors#layer-height-b");
|
||||
optgroup->append_single_option_line("dithering_z_step_size", "others_settings_mixed_colors#z-step-size");
|
||||
optgroup->append_single_option_line("dithering_step_painted_zones_only", "others_settings_mixed_colors#painted-zones-only");
|
||||
|
||||
optgroup = page->new_optgroup(L("Fuzzy Skin"), L"fuzzy_skin");
|
||||
optgroup->append_single_option_line("fuzzy_skin", "others_settings_fuzzy_skin");
|
||||
optgroup->append_single_option_line("fuzzy_skin_mode", "others_settings_fuzzy_skin#fuzzy-skin-mode");
|
||||
|
||||
Reference in New Issue
Block a user