mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 10:21:00 +00:00
BBL Port Color Mix Base
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "FilamentMixer.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <cfloat>
|
||||
@@ -886,7 +887,12 @@ bool verify_update_print_object_regions(
|
||||
size_t hash = regions[i]->config_hash();
|
||||
size_t j = i;
|
||||
for (++ j; j < regions.size() && regions[j]->config_hash() == hash; ++ j)
|
||||
if (regions[i]->config() == regions[j]->config()) {
|
||||
// Same config but different gradient_volume_id is intentional (per-part gradient
|
||||
// splitting) and must NOT be flagged as a merge. When per-part is off all regions
|
||||
// carry an invalid (default) gradient_volume_id, so the AND condition is always
|
||||
// true and behavior matches the legacy check.
|
||||
if (regions[i]->config() == regions[j]->config()
|
||||
&& regions[i]->gradient_volume_id() == regions[j]->gradient_volume_id()) {
|
||||
// Regions were merged. We need to reslice.
|
||||
return false;
|
||||
}
|
||||
@@ -978,7 +984,10 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
const float xy_contour_compensation,
|
||||
const std::vector<unsigned int> &painting_extruders,
|
||||
std::vector<int> &variant_index,
|
||||
const bool has_painted_fuzzy_skin)
|
||||
const bool has_painted_fuzzy_skin,
|
||||
// Per-part gradient: slot_per_part_enabled[s-1] is true when mixed slot s has
|
||||
// filament_mixed_gradient_per_part on. Empty / all-false preserves legacy behavior.
|
||||
const std::vector<bool> &slot_per_part_enabled = {})
|
||||
{
|
||||
// Reuse the old object or generate a new one.
|
||||
auto out = print_object_regions_old ? std::unique_ptr<PrintObjectRegions>(print_object_regions_old) : std::make_unique<PrintObjectRegions>();
|
||||
@@ -1013,19 +1022,71 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
update_volume_bboxes(layer_ranges_regions, out->cached_volume_ids, model_volumes, out->trafo_bboxes, is_mm_painted ? 0.f : std::max(0.f, xy_contour_compensation));
|
||||
|
||||
std::vector<PrintRegion*> region_set;
|
||||
auto get_create_region = [®ion_set, &all_regions](PrintRegionConfig &&config) -> PrintRegion* {
|
||||
// Look up or create a PrintRegion. The optional volume_tag, when valid (non-zero ObjectID),
|
||||
// keys the region to one ModelVolume so two volumes with identical settings still get
|
||||
// separate regions — needed so each part can run its own gradient. A default (invalid)
|
||||
// tag reproduces the previous lookup exactly.
|
||||
auto get_create_region = [®ion_set, &all_regions](PrintRegionConfig &&config, ObjectID volume_tag = ObjectID()) -> PrintRegion* {
|
||||
size_t hash = config.hash();
|
||||
auto it = Slic3r::lower_bound_by_predicate(region_set.begin(), region_set.end(), [&config, hash](const PrintRegion* l) {
|
||||
return l->config_hash() < hash || (l->config_hash() == hash && l->config() < config); });
|
||||
if (it != region_set.end() && (*it)->config_hash() == hash && (*it)->config() == config)
|
||||
auto it = Slic3r::lower_bound_by_predicate(region_set.begin(), region_set.end(), [&config, hash, volume_tag](const PrintRegion* l) {
|
||||
return l->config_hash() < hash || (l->config_hash() == hash && l->config() < config)
|
||||
|| (l->config_hash() == hash && l->config() == config && l->gradient_volume_id() < volume_tag); });
|
||||
if (it != region_set.end() && (*it)->config_hash() == hash && (*it)->config() == config
|
||||
&& (*it)->gradient_volume_id() == volume_tag)
|
||||
return *it;
|
||||
// Insert into a sorted array, it has O(n) complexity, but the calling algorithm has an O(n^2*log(n)) complexity anyways.
|
||||
all_regions.emplace_back(std::make_unique<PrintRegion>(std::move(config), hash, int(all_regions.size())));
|
||||
all_regions.emplace_back(std::make_unique<PrintRegion>(std::move(config), hash, int(all_regions.size()), volume_tag));
|
||||
PrintRegion *region = all_regions.back().get();
|
||||
region_set.emplace(it, region);
|
||||
return region;
|
||||
};
|
||||
|
||||
// Per-part gradient: count how many model-part volumes in this object use each
|
||||
// per-part-enabled gradient slot. Only slots with at least 2 users get their volumes
|
||||
// tagged — a single-user slot gains nothing from per-volume splitting and would only
|
||||
// inflate the region count. Empty slot_per_part_enabled leaves this empty, so
|
||||
// compute_volume_tag below always returns an invalid tag and nothing changes.
|
||||
std::vector<int> per_part_volume_users;
|
||||
if (!slot_per_part_enabled.empty()) {
|
||||
per_part_volume_users.assign(slot_per_part_enabled.size(), 0);
|
||||
for (const ModelVolume *mv : model_volumes) {
|
||||
if (! mv->is_model_part())
|
||||
continue;
|
||||
const DynamicPrintConfig *range_cfg = layer_ranges_regions.empty() ? nullptr : layer_ranges_regions.front().config;
|
||||
PrintRegionConfig vol_cfg = region_config_from_model_volume(default_region_config, range_cfg, *mv, num_extruders, variant_index);
|
||||
for (unsigned int s_1based : { (unsigned int)vol_cfg.outer_wall_filament_id.value,
|
||||
(unsigned int)vol_cfg.inner_wall_filament_id.value,
|
||||
(unsigned int)vol_cfg.sparse_infill_filament_id.value,
|
||||
(unsigned int)vol_cfg.internal_solid_filament_id.value,
|
||||
(unsigned int)vol_cfg.top_surface_filament_id.value,
|
||||
(unsigned int)vol_cfg.bottom_surface_filament_id.value }) {
|
||||
if (s_1based >= 1
|
||||
&& size_t(s_1based - 1) < slot_per_part_enabled.size()
|
||||
&& slot_per_part_enabled[s_1based - 1])
|
||||
++per_part_volume_users[s_1based - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
auto compute_volume_tag = [&](const PrintRegionConfig &cfg, const ModelVolume &mv) -> ObjectID {
|
||||
if (per_part_volume_users.empty())
|
||||
return ObjectID();
|
||||
auto qualifies = [&](unsigned int s_1based) {
|
||||
return s_1based >= 1
|
||||
&& size_t(s_1based - 1) < slot_per_part_enabled.size()
|
||||
&& slot_per_part_enabled[s_1based - 1]
|
||||
&& per_part_volume_users[s_1based - 1] >= 2;
|
||||
};
|
||||
if (qualifies((unsigned int)cfg.outer_wall_filament_id.value)
|
||||
|| qualifies((unsigned int)cfg.inner_wall_filament_id.value)
|
||||
|| qualifies((unsigned int)cfg.sparse_infill_filament_id.value)
|
||||
|| qualifies((unsigned int)cfg.internal_solid_filament_id.value)
|
||||
|| qualifies((unsigned int)cfg.top_surface_filament_id.value)
|
||||
|| qualifies((unsigned int)cfg.bottom_surface_filament_id.value)) {
|
||||
return mv.id();
|
||||
}
|
||||
return ObjectID();
|
||||
};
|
||||
|
||||
// Chain the regions in the order they are stored in the volumes list.
|
||||
for (int volume_id = 0; volume_id < int(model_volumes.size()); ++ volume_id) {
|
||||
const ModelVolume &volume = *model_volumes[volume_id];
|
||||
@@ -1034,9 +1095,11 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
if (const PrintObjectRegions::BoundingBox *bbox = find_volume_extents(layer_range, volume); bbox) {
|
||||
if (volume.is_model_part()) {
|
||||
// Add a model volume, assign an existing region or generate a new one.
|
||||
PrintRegionConfig vol_cfg = region_config_from_model_volume(default_region_config, layer_range.config, volume, num_extruders, variant_index);
|
||||
ObjectID volume_tag = compute_volume_tag(vol_cfg, volume);
|
||||
layer_range.volume_regions.push_back({
|
||||
&volume, -1,
|
||||
get_create_region(region_config_from_model_volume(default_region_config, layer_range.config, volume, num_extruders, variant_index)),
|
||||
get_create_region(std::move(vol_cfg), volume_tag),
|
||||
bbox
|
||||
});
|
||||
} else if (volume.is_negative_volume()) {
|
||||
@@ -1121,6 +1184,12 @@ static PrintObjectRegions* generate_print_object_regions(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Save the slot_per_part_enabled bit vector that produced these regions, so the guard in
|
||||
// Print::apply can detect changes on the next call even when PrintRegionConfig did not
|
||||
// change. Always written — including an empty vector — so the snapshot always reflects
|
||||
// the exact input used to generate the current regions.
|
||||
out->last_slot_per_part_enabled = slot_per_part_enabled;
|
||||
return out.release();
|
||||
}
|
||||
|
||||
@@ -1141,6 +1210,17 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
std::vector <unsigned int> used_filaments = this->extruders(true);
|
||||
std::unordered_set <unsigned int> used_filament_set(used_filaments.begin(), used_filaments.end());
|
||||
|
||||
// A mixed slot is virtual: the filaments actually consumed are its components, so add them
|
||||
// to the used set or they would be treated as unused and stripped from the config.
|
||||
{
|
||||
auto* is_mixed_opt = new_full_config.option<ConfigOptionBools>("filament_is_mixed");
|
||||
auto* comp_strs_opt = new_full_config.option<ConfigOptionStrings>("filament_mixed_components");
|
||||
if (is_mixed_opt && comp_strs_opt && has_any_mixed_filament(is_mixed_opt->values)) {
|
||||
auto expanded = expand_mixed_filaments(used_filaments, is_mixed_opt->values, comp_strs_opt->values);
|
||||
used_filament_set.insert(expanded.begin(), expanded.end());
|
||||
}
|
||||
}
|
||||
|
||||
//new_full_config.normalize_fdm(used_filaments);
|
||||
new_full_config.normalize_fdm_1();
|
||||
t_config_option_keys changed_keys = new_full_config.normalize_fdm_2(objects().size(), used_filaments.size());
|
||||
@@ -1802,6 +1882,29 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
update_filament_self_index_cache();
|
||||
}
|
||||
|
||||
// Per-part gradient: compute the per-slot enable bit vector once for this Print::apply pass.
|
||||
// Used by generate_print_object_regions to decide which volumes deserve their own PrintRegion.
|
||||
std::vector<bool> slot_per_part_enabled;
|
||||
{
|
||||
const auto &is_mixed_vec = m_config.filament_is_mixed.values;
|
||||
const auto &grad_vec = m_config.filament_mixed_gradient.values;
|
||||
const auto &per_part_vec = m_config.filament_mixed_gradient_per_part.values;
|
||||
const auto &components_vec = m_config.filament_mixed_components.values;
|
||||
slot_per_part_enabled.assign(is_mixed_vec.size(), false);
|
||||
for (size_t i = 0; i < is_mixed_vec.size(); ++i) {
|
||||
if (! is_mixed_vec[i])
|
||||
continue;
|
||||
std::vector<unsigned int> comps = parse_mixed_components(i < components_vec.size() ? components_vec[i] : "");
|
||||
if (comps.size() != 2)
|
||||
continue;
|
||||
if (i >= grad_vec.size() || ! grad_vec[i])
|
||||
continue;
|
||||
if (i >= per_part_vec.size() || ! per_part_vec[i])
|
||||
continue;
|
||||
slot_per_part_enabled[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// All regions now have distinct settings.
|
||||
// Check whether applying the new region config defaults we would get different regions,
|
||||
// update regions or create regions from scratch.
|
||||
@@ -1862,6 +1965,15 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
|
||||
},
|
||||
print_variant_index)) {
|
||||
// Per-part gradient: PrintRegionConfig alone cannot reveal a change in which slots
|
||||
// have per-part enabled, so compare against the snapshot taken when these regions
|
||||
// were generated and regenerate on any difference (slot toggled, per-part moved
|
||||
// between slots, eligibility changed via components / gradient / is_mixed).
|
||||
if (print_object_regions->last_slot_per_part_enabled != slot_per_part_enabled) {
|
||||
invalidate();
|
||||
model_object_status.print_object_regions_status = ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid;
|
||||
print_regions_reshuffled = true;
|
||||
}
|
||||
// Regions are valid, just keep them.
|
||||
} else {
|
||||
// Regions were reshuffled.
|
||||
@@ -1884,7 +1996,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
||||
print_object.is_mm_painted() ? 0.f : float(print_object.config().xy_contour_compensation.value),
|
||||
painting_extruders,
|
||||
print_variant_index,
|
||||
print_object.is_fuzzy_skin_painted());
|
||||
print_object.is_fuzzy_skin_painted(),
|
||||
slot_per_part_enabled);
|
||||
}
|
||||
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
||||
if ((*it)->m_shared_regions) {
|
||||
|
||||
Reference in New Issue
Block a user