mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 03:11:47 +00:00
- Adds optional whole-object Local-Z support for mixed wall regions instead of limiting Local-Z strictly to painted mixed zones.
- Adds an experimental direct multicolor Local-Z solver with carry-over error for 3+ color mixed rows, reducing pair-cadence banding.
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
# Snapmaker Orca FullSpectrum v0.9.8
|
||||||
|
|
||||||
|
Local-Z multicolor improvements, duplicate gap-fill cleanup, and mixed-filament workflow fixes.
|
||||||
|
Compared to the previous FullSpectrum v0.9.7 release.
|
||||||
|
Based on Snapmaker Orca v2.3.1, with the bundled print-preprocessing Flutter/web resources kept on the newer 2.3.12 line.
|
||||||
|
|
||||||
|
## Quick Overview
|
||||||
|
|
||||||
|
- Adds optional whole-object Local-Z support for mixed wall regions instead of limiting Local-Z strictly to painted mixed zones.
|
||||||
|
- Adds an experimental direct multicolor Local-Z solver with carry-over error for 3+ color mixed rows, reducing pair-cadence banding.
|
||||||
|
- Fixes several Local-Z boundary issues so painted colors, fixed-color islands, and whole-object mixed masks cooperate more cleanly.
|
||||||
|
- Fixes a duplicated inner-loop gap-fill regression that could overprint narrow collapsed regions.
|
||||||
|
- Fixes mixed-filament bias-state and print-preprocessing remap issues that required restarting or blocked otherwise valid remaps.
|
||||||
|
|
||||||
|
## More Detailed Overview
|
||||||
|
|
||||||
|
### Local-Z Dithering
|
||||||
|
|
||||||
|
- Restores and relabels the Local-Z lower and upper height bounds in the Others tab so they are clearly presented as Local-Z controls.
|
||||||
|
- Adds `Apply Local-Z to whole mixed objects` as an experimental opt-in, allowing default mixed wall regions to keep Local-Z active outside painted islands.
|
||||||
|
- Adds `Use direct multicolor Local-Z solver` as an experimental opt-in for 3+ color rows, using direct component allocation with carry-over error across layers instead of collapsing everything into pair cadence first.
|
||||||
|
- Keeps the older pair-based Local-Z path as the default for compatibility, while letting the new direct solver be tested independently.
|
||||||
|
- Fixes several planner/runtime issues around painted zones inside whole-object Local-Z bodies, including lost painted Local-Z treatment, phase resets at zone boundaries, and overlap/regrowth artifacts at fixed-color painted caps.
|
||||||
|
- Improves Local-Z summary text in the mixed-filament panel so the UI reports when the direct multicolor solver is active instead of describing the older pair-cadence model.
|
||||||
|
|
||||||
|
### Gap Fill And Toolpath Generation
|
||||||
|
|
||||||
|
- Removes the collapsed infill gap-fill fallback that was appending duplicate gap-fill passes in narrow looped regions.
|
||||||
|
- Resolves the regression where certain capsule-like inner loops could receive a doubled gap-fill contour and get visibly over-squished.
|
||||||
|
- Keeps the safer older path that avoided the duplicate loop in the reproduced meshes.
|
||||||
|
|
||||||
|
### Mixed-Filament Workflow Fixes
|
||||||
|
|
||||||
|
- Fixes mixed-filament bias controls getting stuck disabled after loading certain `.3mf` projects and then creating a new project without restarting.
|
||||||
|
- Keeps the mixed-filament project config mirrored back into the sidebar state when presets or projects are reloaded programmatically.
|
||||||
|
- Fixes print-preprocessing filament remap for the newer web dialog by sending file-side nozzle diameter data expected by the 2.3.12 frontend.
|
||||||
|
- Reduces the mismatch between automatic tray selection and manual remap eligibility in the preprocessing flow.
|
||||||
|
|
||||||
|
### UI Cleanup
|
||||||
|
|
||||||
|
- Retires the old height-weighted cadence and pointillisme-facing controls from the Others tab while keeping compatibility for older stored configs and projects.
|
||||||
|
- Keeps the Local-Z-related controls that are still in active use visible and synchronized with the current mixed-filament workflow.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `cmake --build build --target Snapmaker_Orca --config Release --parallel 4` succeeded.
|
||||||
|
- Much of this release was validated through repeated slice-preview and hardware-oriented regression checks for mixed-filament and Local-Z behavior.
|
||||||
|
- The active build tree in this workspace still had no registered `ctest` cases, so broader automated test reruns were not available from `ctest` here.
|
||||||
|
- macOS builds from this fork remain unsigned and not notarized.
|
||||||
|
- Release downloads for this fork are available here: https://github.com/ratdoux/OrcaSlicer-FullSpectrum/releases
|
||||||
|
- Use at your own risk.
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#define SLIC3R_APP_KEY "Snapmaker_Orca"
|
#define SLIC3R_APP_KEY "Snapmaker_Orca"
|
||||||
#define SLIC3R_VERSION "01.10.01.70"
|
#define SLIC3R_VERSION "01.10.01.70"
|
||||||
#define Snapmaker_VERSION "2.3.1"
|
#define Snapmaker_VERSION "2.3.1"
|
||||||
#define FULLSPECTRUM_VERSION "0.9.7"
|
#define FULLSPECTRUM_VERSION "0.9.8"
|
||||||
#define MIN_FIRM_VER "1.0.0"
|
#define MIN_FIRM_VER "1.0.0"
|
||||||
#ifndef GIT_COMMIT_HASH
|
#ifndef GIT_COMMIT_HASH
|
||||||
#define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized
|
#define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized
|
||||||
|
|||||||
+36
-7
@@ -4865,6 +4865,7 @@ LayerResult GCode::process_layer(const Print& print,
|
|||||||
constexpr double LOCAL_Z_BASE_MASK_EXPAND_MM = 0.04;
|
constexpr double LOCAL_Z_BASE_MASK_EXPAND_MM = 0.04;
|
||||||
const float local_z_perimeter_mask_expand = float(scale_(LOCAL_Z_PERIMETER_MASK_EXPAND_MM));
|
const float local_z_perimeter_mask_expand = float(scale_(LOCAL_Z_PERIMETER_MASK_EXPAND_MM));
|
||||||
const float local_z_base_mask_expand = float(scale_(LOCAL_Z_BASE_MASK_EXPAND_MM));
|
const float local_z_base_mask_expand = float(scale_(LOCAL_Z_BASE_MASK_EXPAND_MM));
|
||||||
|
const bool local_z_whole_objects_enabled = print.full_print_config().opt_bool("dithering_local_z_whole_objects");
|
||||||
|
|
||||||
struct LocalZPassBucket {
|
struct LocalZPassBucket {
|
||||||
const SubLayerPlan* plan { nullptr };
|
const SubLayerPlan* plan { nullptr };
|
||||||
@@ -4933,17 +4934,45 @@ LayerResult GCode::process_layer(const Print& print,
|
|||||||
|
|
||||||
LocalZPassBucket bucket;
|
LocalZPassBucket bucket;
|
||||||
bucket.plan = &plan;
|
bucket.plan = &plan;
|
||||||
bucket.compensated_masks_by_extruder.assign(plan.painted_masks_by_extruder.size(), ExPolygons());
|
const size_t plan_mask_slots = std::max(plan.painted_masks_by_extruder.size(), plan.fixed_painted_masks_by_extruder.size());
|
||||||
|
bucket.compensated_masks_by_extruder.assign(plan_mask_slots, ExPolygons());
|
||||||
bool pass_has_raw_masks = false;
|
bool pass_has_raw_masks = false;
|
||||||
bool pass_has_compensated_masks = false;
|
bool pass_has_compensated_masks = false;
|
||||||
for (size_t extruder_id = 0; extruder_id < plan.painted_masks_by_extruder.size(); ++extruder_id) {
|
ExPolygons fixed_raw_masks_union;
|
||||||
const ExPolygons& raw_masks = plan.painted_masks_by_extruder[extruder_id];
|
for (const ExPolygons &fixed_masks : plan.fixed_painted_masks_by_extruder)
|
||||||
if (raw_masks.empty())
|
if (!fixed_masks.empty())
|
||||||
|
append(fixed_raw_masks_union, fixed_masks);
|
||||||
|
if (!fixed_raw_masks_union.empty() && fixed_raw_masks_union.size() > 1)
|
||||||
|
fixed_raw_masks_union = union_ex(fixed_raw_masks_union);
|
||||||
|
const ExPolygons fixed_compensated_guard =
|
||||||
|
fixed_raw_masks_union.empty() ? ExPolygons() : local_z_compensate_masks(fixed_raw_masks_union, local_z_perimeter_mask_expand, true);
|
||||||
|
|
||||||
|
for (size_t extruder_id = 0; extruder_id < plan_mask_slots; ++extruder_id) {
|
||||||
|
const ExPolygons mixed_raw_masks =
|
||||||
|
extruder_id < plan.painted_masks_by_extruder.size() ? plan.painted_masks_by_extruder[extruder_id] : ExPolygons();
|
||||||
|
const ExPolygons fixed_raw_masks =
|
||||||
|
extruder_id < plan.fixed_painted_masks_by_extruder.size() ? plan.fixed_painted_masks_by_extruder[extruder_id] : ExPolygons();
|
||||||
|
if (mixed_raw_masks.empty() && fixed_raw_masks.empty())
|
||||||
continue;
|
continue;
|
||||||
pass_has_raw_masks = true;
|
pass_has_raw_masks = true;
|
||||||
raw_mask_polygon_count += raw_masks.size();
|
raw_mask_polygon_count += mixed_raw_masks.size() + fixed_raw_masks.size();
|
||||||
append(raw_mixed_masks_union, raw_masks);
|
if (!mixed_raw_masks.empty())
|
||||||
ExPolygons compensated = local_z_compensate_masks(raw_masks, local_z_perimeter_mask_expand, true);
|
append(raw_mixed_masks_union, mixed_raw_masks);
|
||||||
|
if (!fixed_raw_masks.empty())
|
||||||
|
append(raw_mixed_masks_union, fixed_raw_masks);
|
||||||
|
|
||||||
|
ExPolygons compensated;
|
||||||
|
if (!mixed_raw_masks.empty()) {
|
||||||
|
ExPolygons compensated_mixed = local_z_compensate_masks(mixed_raw_masks, local_z_perimeter_mask_expand, true);
|
||||||
|
if (local_z_whole_objects_enabled && !fixed_compensated_guard.empty())
|
||||||
|
compensated_mixed = diff_ex(compensated_mixed, fixed_compensated_guard);
|
||||||
|
if (!compensated_mixed.empty())
|
||||||
|
append(compensated, compensated_mixed);
|
||||||
|
}
|
||||||
|
if (!fixed_raw_masks.empty())
|
||||||
|
append(compensated, fixed_raw_masks);
|
||||||
|
if (!compensated.empty() && compensated.size() > 1)
|
||||||
|
compensated = union_ex(compensated);
|
||||||
if (!compensated.empty()) {
|
if (!compensated.empty()) {
|
||||||
pass_has_compensated_masks = true;
|
pass_has_compensated_masks = true;
|
||||||
compensated_mask_polygon_count += compensated.size();
|
compensated_mask_polygon_count += compensated.size();
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ struct MixedFilamentPreviewSettings
|
|||||||
double preferred_a_height { 0.0 };
|
double preferred_a_height { 0.0 };
|
||||||
double preferred_b_height { 0.0 };
|
double preferred_b_height { 0.0 };
|
||||||
bool local_z_mode { false };
|
bool local_z_mode { false };
|
||||||
|
bool local_z_direct_multicolor { false };
|
||||||
size_t wall_loops { 1 };
|
size_t wall_loops { 1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ static std::vector<std::string> s_project_options {
|
|||||||
"mixed_color_layer_height_b",
|
"mixed_color_layer_height_b",
|
||||||
"dithering_z_step_size",
|
"dithering_z_step_size",
|
||||||
"dithering_local_z_mode",
|
"dithering_local_z_mode",
|
||||||
|
"dithering_local_z_whole_objects",
|
||||||
|
"dithering_local_z_direct_multicolor",
|
||||||
"dithering_step_painted_zones_only",
|
"dithering_step_painted_zones_only",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -292,6 +292,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
|| opt_key == "dithering_z_step_size"
|
|| opt_key == "dithering_z_step_size"
|
||||||
|| opt_key == "dithering_local_z_mode"
|
|| opt_key == "dithering_local_z_mode"
|
||||||
|| opt_key == "dithering_local_z_whole_objects"
|
|| opt_key == "dithering_local_z_whole_objects"
|
||||||
|
|| opt_key == "dithering_local_z_direct_multicolor"
|
||||||
|| opt_key == "dithering_step_painted_zones_only"
|
|| opt_key == "dithering_step_painted_zones_only"
|
||||||
|| opt_key == "mixed_filament_gradient_mode"
|
|| opt_key == "mixed_filament_gradient_mode"
|
||||||
|| opt_key == "mixed_filament_height_lower_bound"
|
|| opt_key == "mixed_filament_height_lower_bound"
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ struct SubLayerPlan
|
|||||||
double print_z { 0.0 };
|
double print_z { 0.0 };
|
||||||
double flow_height { 0.0 };
|
double flow_height { 0.0 };
|
||||||
std::vector<ExPolygons> painted_masks_by_extruder;
|
std::vector<ExPolygons> painted_masks_by_extruder;
|
||||||
|
std::vector<ExPolygons> fixed_painted_masks_by_extruder;
|
||||||
ExPolygons base_masks;
|
ExPolygons base_masks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1205,6 +1205,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
new_full_config.option("dithering_z_step_size", true);
|
new_full_config.option("dithering_z_step_size", true);
|
||||||
new_full_config.option("dithering_local_z_mode", true);
|
new_full_config.option("dithering_local_z_mode", true);
|
||||||
new_full_config.option("dithering_local_z_whole_objects", true);
|
new_full_config.option("dithering_local_z_whole_objects", true);
|
||||||
|
new_full_config.option("dithering_local_z_direct_multicolor", true);
|
||||||
new_full_config.option("dithering_step_painted_zones_only", true);
|
new_full_config.option("dithering_step_painted_zones_only", true);
|
||||||
new_full_config.option("mixed_filament_gradient_mode", true);
|
new_full_config.option("mixed_filament_gradient_mode", true);
|
||||||
new_full_config.option("mixed_filament_height_lower_bound", true);
|
new_full_config.option("mixed_filament_height_lower_bound", true);
|
||||||
@@ -1219,6 +1220,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
m_config.option("dithering_z_step_size", true);
|
m_config.option("dithering_z_step_size", true);
|
||||||
m_config.option("dithering_local_z_mode", true);
|
m_config.option("dithering_local_z_mode", true);
|
||||||
m_config.option("dithering_local_z_whole_objects", true);
|
m_config.option("dithering_local_z_whole_objects", true);
|
||||||
|
m_config.option("dithering_local_z_direct_multicolor", true);
|
||||||
m_config.option("dithering_step_painted_zones_only", true);
|
m_config.option("dithering_step_painted_zones_only", true);
|
||||||
m_config.option("mixed_filament_gradient_mode", true);
|
m_config.option("mixed_filament_gradient_mode", true);
|
||||||
m_config.option("mixed_filament_height_lower_bound", true);
|
m_config.option("mixed_filament_height_lower_bound", true);
|
||||||
@@ -1233,6 +1235,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||||||
m_default_object_config.option("dithering_z_step_size", true);
|
m_default_object_config.option("dithering_z_step_size", true);
|
||||||
m_default_object_config.option("dithering_local_z_mode", true);
|
m_default_object_config.option("dithering_local_z_mode", true);
|
||||||
m_default_object_config.option("dithering_local_z_whole_objects", true);
|
m_default_object_config.option("dithering_local_z_whole_objects", true);
|
||||||
|
m_default_object_config.option("dithering_local_z_direct_multicolor", true);
|
||||||
m_default_object_config.option("dithering_step_painted_zones_only", true);
|
m_default_object_config.option("dithering_step_painted_zones_only", true);
|
||||||
m_default_object_config.option("mixed_filament_gradient_mode", true);
|
m_default_object_config.option("mixed_filament_gradient_mode", true);
|
||||||
m_default_object_config.option("mixed_filament_height_lower_bound", true);
|
m_default_object_config.option("mixed_filament_height_lower_bound", true);
|
||||||
|
|||||||
@@ -4300,6 +4300,14 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(false));
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
|
def = this->add("dithering_local_z_direct_multicolor", coBool);
|
||||||
|
def->label = L("Use direct multicolor Local-Z solver");
|
||||||
|
def->category = L("Others");
|
||||||
|
def->tooltip = L("Experimental. For mixed rows with 3 or more physical filaments, allocate Local-Z sublayers directly across all components with carry-over error between layers instead of collapsing them into pair cadence.\n\n"
|
||||||
|
"This can reduce visible banding in multicolor Local-Z blends at the cost of more toolchanges. It is ignored when explicit Local-Z A/B heights are set.");
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
def = this->add("dithering_step_painted_zones_only", coBool);
|
def = this->add("dithering_step_painted_zones_only", coBool);
|
||||||
def->label = L("Use step size in painted zones only");
|
def->label = L("Use step size in painted zones only");
|
||||||
def->category = L("Others");
|
def->category = L("Others");
|
||||||
|
|||||||
@@ -1369,6 +1369,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||||||
((ConfigOptionFloat, dithering_z_step_size))
|
((ConfigOptionFloat, dithering_z_step_size))
|
||||||
((ConfigOptionBool, dithering_local_z_mode))
|
((ConfigOptionBool, dithering_local_z_mode))
|
||||||
((ConfigOptionBool, dithering_local_z_whole_objects))
|
((ConfigOptionBool, dithering_local_z_whole_objects))
|
||||||
|
((ConfigOptionBool, dithering_local_z_direct_multicolor))
|
||||||
((ConfigOptionBool, dithering_step_painted_zones_only))
|
((ConfigOptionBool, dithering_step_painted_zones_only))
|
||||||
((ConfigOptionString, printer_model))
|
((ConfigOptionString, printer_model))
|
||||||
((ConfigOptionFloat, resolution))
|
((ConfigOptionFloat, resolution))
|
||||||
|
|||||||
@@ -950,6 +950,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||||||
|| opt_key == "dithering_z_step_size"
|
|| opt_key == "dithering_z_step_size"
|
||||||
|| opt_key == "dithering_local_z_mode"
|
|| opt_key == "dithering_local_z_mode"
|
||||||
|| opt_key == "dithering_local_z_whole_objects"
|
|| opt_key == "dithering_local_z_whole_objects"
|
||||||
|
|| opt_key == "dithering_local_z_direct_multicolor"
|
||||||
|| opt_key == "dithering_step_painted_zones_only"
|
|| opt_key == "dithering_step_painted_zones_only"
|
||||||
|| opt_key == "mixed_filament_component_bias_enabled"
|
|| opt_key == "mixed_filament_component_bias_enabled"
|
||||||
|| opt_key == "mixed_filament_region_collapse"
|
|| opt_key == "mixed_filament_region_collapse"
|
||||||
|
|||||||
@@ -1297,6 +1297,28 @@ static std::vector<double> build_uniform_local_z_pass_heights(double base_height
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<double> build_uniform_local_z_pass_heights_exact(double base_height,
|
||||||
|
double lower_bound,
|
||||||
|
double upper_bound,
|
||||||
|
size_t pass_count)
|
||||||
|
{
|
||||||
|
if (base_height <= EPSILON || pass_count == 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const double lo = std::max<double>(0.01, lower_bound);
|
||||||
|
const double hi = std::max<double>(lo, upper_bound);
|
||||||
|
if (pass_count == 1)
|
||||||
|
return { base_height };
|
||||||
|
|
||||||
|
if (double(pass_count) * lo > base_height + EPSILON || double(pass_count) * hi < base_height - EPSILON)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<double> out(pass_count, base_height / double(pass_count));
|
||||||
|
if (!fit_pass_heights_to_interval(out, base_height, lo, hi))
|
||||||
|
return {};
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void compute_local_z_gradient_component_heights(int mix_b_percent, double lower_bound, double upper_bound,
|
static inline void compute_local_z_gradient_component_heights(int mix_b_percent, double lower_bound, double upper_bound,
|
||||||
double &h_a, double &h_b)
|
double &h_a, double &h_b)
|
||||||
{
|
{
|
||||||
@@ -1891,6 +1913,29 @@ static bool local_z_eligible_mixed_row(const MixedFilament &mf)
|
|||||||
mf.distribution_mode != int(MixedFilament::SameLayerPointillisme);
|
mf.distribution_mode != int(MixedFilament::SameLayerPointillisme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool local_z_direct_multicolor_row(const MixedFilament &mf,
|
||||||
|
size_t num_physical,
|
||||||
|
std::vector<unsigned int> *component_ids = nullptr,
|
||||||
|
std::vector<int> *component_weights = nullptr)
|
||||||
|
{
|
||||||
|
if (!local_z_eligible_mixed_row(mf))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::vector<unsigned int> ids = decode_gradient_component_ids(mf, num_physical);
|
||||||
|
if (ids.size() < 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (component_ids != nullptr)
|
||||||
|
*component_ids = ids;
|
||||||
|
if (component_weights != nullptr) {
|
||||||
|
std::vector<int> weights = decode_gradient_component_weights(mf, ids.size());
|
||||||
|
if (weights.empty())
|
||||||
|
weights.assign(ids.size(), 1);
|
||||||
|
*component_weights = std::move(weights);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct LocalZActivePair
|
struct LocalZActivePair
|
||||||
{
|
{
|
||||||
unsigned int component_a = 0;
|
unsigned int component_a = 0;
|
||||||
@@ -1998,6 +2043,170 @@ static std::vector<LocalZActivePair> build_local_z_pair_cycle_for_row(const Mixe
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<double> build_local_z_direct_multicolor_pass_heights(const MixedFilament &mf,
|
||||||
|
const std::vector<int> &component_weights,
|
||||||
|
double base_height,
|
||||||
|
double lower_bound,
|
||||||
|
double upper_bound,
|
||||||
|
size_t component_count)
|
||||||
|
{
|
||||||
|
if (base_height <= EPSILON || component_count == 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const double lo = std::max<double>(0.01, lower_bound);
|
||||||
|
const double hi = std::max<double>(lo, upper_bound);
|
||||||
|
const size_t min_passes = size_t(std::max<double>(1.0, std::ceil((base_height - EPSILON) / hi)));
|
||||||
|
const size_t max_passes = size_t(std::max<double>(1.0, std::floor((base_height + EPSILON) / lo)));
|
||||||
|
if (max_passes == 0)
|
||||||
|
return { base_height };
|
||||||
|
|
||||||
|
std::vector<int> positive_weights;
|
||||||
|
positive_weights.reserve(component_weights.size());
|
||||||
|
for (const int weight : component_weights)
|
||||||
|
if (weight > 0)
|
||||||
|
positive_weights.emplace_back(weight);
|
||||||
|
if (positive_weights.empty())
|
||||||
|
positive_weights.assign(component_count, 1);
|
||||||
|
|
||||||
|
const int total_weight = std::max(1, std::accumulate(positive_weights.begin(), positive_weights.end(), 0));
|
||||||
|
std::vector<double> component_targets;
|
||||||
|
component_targets.reserve(positive_weights.size());
|
||||||
|
size_t ideal_passes = 0;
|
||||||
|
for (const int weight : positive_weights) {
|
||||||
|
const double target = base_height * double(weight) / double(total_weight);
|
||||||
|
component_targets.emplace_back(target);
|
||||||
|
ideal_passes += size_t(std::max<double>(1.0, std::ceil((target - EPSILON) / hi)));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pass_limit = max_passes;
|
||||||
|
if (mf.local_z_max_sublayers >= 2)
|
||||||
|
pass_limit = std::min(pass_limit, size_t(std::max(2, mf.local_z_max_sublayers)));
|
||||||
|
pass_limit = std::max(pass_limit, min_passes);
|
||||||
|
|
||||||
|
size_t desired_passes = std::clamp(std::max(component_targets.size(), ideal_passes), min_passes, pass_limit);
|
||||||
|
|
||||||
|
std::vector<double> bins = component_targets;
|
||||||
|
while (bins.size() > desired_passes) {
|
||||||
|
std::sort(bins.begin(), bins.end());
|
||||||
|
const double merged = bins[0] + bins[1];
|
||||||
|
bins.erase(bins.begin(), bins.begin() + 2);
|
||||||
|
bins.emplace_back(merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (bins.size() < desired_passes) {
|
||||||
|
auto it = std::max_element(bins.begin(), bins.end());
|
||||||
|
if (it == bins.end())
|
||||||
|
break;
|
||||||
|
const double value = *it;
|
||||||
|
if (value < 2.0 * lo - EPSILON)
|
||||||
|
break;
|
||||||
|
double first = std::clamp(value * 0.5, lo, value - lo);
|
||||||
|
double second = value - first;
|
||||||
|
if (first < lo - EPSILON || second < lo - EPSILON)
|
||||||
|
break;
|
||||||
|
*it = first;
|
||||||
|
bins.emplace_back(second);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bins.empty())
|
||||||
|
return build_uniform_local_z_pass_heights(base_height, lo, hi, desired_passes);
|
||||||
|
|
||||||
|
std::sort(bins.begin(), bins.end(), std::greater<double>());
|
||||||
|
for (double &value : bins)
|
||||||
|
value = std::clamp(value, lo, hi);
|
||||||
|
if (fit_pass_heights_to_interval(bins, base_height, lo, hi))
|
||||||
|
return bins;
|
||||||
|
|
||||||
|
for (size_t pass_count = desired_passes; pass_count >= min_passes; --pass_count) {
|
||||||
|
std::vector<double> exact = build_uniform_local_z_pass_heights_exact(base_height, lower_bound, upper_bound, pass_count);
|
||||||
|
if (!exact.empty())
|
||||||
|
return exact;
|
||||||
|
if (pass_count == min_passes)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return build_uniform_local_z_pass_heights(base_height, lo, hi, desired_passes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<unsigned int> build_local_z_direct_multicolor_sequence(const std::vector<unsigned int> &component_ids,
|
||||||
|
const std::vector<int> &component_weights,
|
||||||
|
const std::vector<double> &pass_heights,
|
||||||
|
std::vector<double> &carry_error_mm)
|
||||||
|
{
|
||||||
|
if (component_ids.empty() || pass_heights.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<unsigned int> filtered_ids;
|
||||||
|
std::vector<int> filtered_weights;
|
||||||
|
filtered_ids.reserve(component_ids.size());
|
||||||
|
filtered_weights.reserve(component_ids.size());
|
||||||
|
for (size_t idx = 0; idx < component_ids.size(); ++idx) {
|
||||||
|
const int weight = idx < component_weights.size() ? std::max(0, component_weights[idx]) : 0;
|
||||||
|
if (weight <= 0)
|
||||||
|
continue;
|
||||||
|
filtered_ids.emplace_back(component_ids[idx]);
|
||||||
|
filtered_weights.emplace_back(weight);
|
||||||
|
}
|
||||||
|
if (filtered_ids.empty()) {
|
||||||
|
filtered_ids = component_ids;
|
||||||
|
filtered_weights.assign(component_ids.size(), 1);
|
||||||
|
}
|
||||||
|
if (filtered_ids.empty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (carry_error_mm.size() != filtered_ids.size())
|
||||||
|
carry_error_mm.assign(filtered_ids.size(), 0.0);
|
||||||
|
|
||||||
|
const double total_height = std::accumulate(pass_heights.begin(), pass_heights.end(), 0.0);
|
||||||
|
const int total_weight = std::max(1, std::accumulate(filtered_weights.begin(), filtered_weights.end(), 0));
|
||||||
|
|
||||||
|
std::vector<double> desired_heights(filtered_ids.size(), 0.0);
|
||||||
|
for (size_t idx = 0; idx < filtered_ids.size(); ++idx)
|
||||||
|
desired_heights[idx] = total_height * double(filtered_weights[idx]) / double(total_weight) + carry_error_mm[idx];
|
||||||
|
|
||||||
|
std::vector<double> assigned_heights(filtered_ids.size(), 0.0);
|
||||||
|
std::vector<unsigned int> sequence;
|
||||||
|
sequence.reserve(pass_heights.size());
|
||||||
|
int previous_choice = -1;
|
||||||
|
|
||||||
|
for (const double pass_height : pass_heights) {
|
||||||
|
size_t best_idx = 0;
|
||||||
|
double best_score = -std::numeric_limits<double>::infinity();
|
||||||
|
double best_need = -std::numeric_limits<double>::infinity();
|
||||||
|
for (size_t idx = 0; idx < filtered_ids.size(); ++idx) {
|
||||||
|
const double remaining_need = desired_heights[idx] - assigned_heights[idx];
|
||||||
|
double score = remaining_need;
|
||||||
|
if (int(idx) == previous_choice)
|
||||||
|
score -= 0.35 * pass_height;
|
||||||
|
|
||||||
|
if (score > best_score + 1e-9 ||
|
||||||
|
(std::abs(score - best_score) <= 1e-9 &&
|
||||||
|
(remaining_need > best_need + 1e-9 ||
|
||||||
|
(std::abs(remaining_need - best_need) <= 1e-9 && filtered_ids[idx] < filtered_ids[best_idx])))) {
|
||||||
|
best_idx = idx;
|
||||||
|
best_score = score;
|
||||||
|
best_need = remaining_need;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assigned_heights[best_idx] += pass_height;
|
||||||
|
previous_choice = int(best_idx);
|
||||||
|
sequence.emplace_back(filtered_ids[best_idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t idx = 0; idx < filtered_ids.size(); ++idx)
|
||||||
|
carry_error_mm[idx] = desired_heights[idx] - assigned_heights[idx];
|
||||||
|
|
||||||
|
const double error_sum = std::accumulate(carry_error_mm.begin(), carry_error_mm.end(), 0.0);
|
||||||
|
if (!carry_error_mm.empty() && std::abs(error_sum) > 1e-9) {
|
||||||
|
const double correction = error_sum / double(carry_error_mm.size());
|
||||||
|
for (double &value : carry_error_mm)
|
||||||
|
value -= correction;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
static LocalZActivePair derive_local_z_active_pair(const MixedFilament &mf,
|
static LocalZActivePair derive_local_z_active_pair(const MixedFilament &mf,
|
||||||
const std::vector<LocalZActivePair> &pair_cycle,
|
const std::vector<LocalZActivePair> &pair_cycle,
|
||||||
size_t num_physical,
|
size_t num_physical,
|
||||||
@@ -2723,6 +2932,92 @@ static std::vector<std::vector<ExPolygons>> local_z_planner_segmentation_with_wh
|
|||||||
return augmented;
|
return augmented;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<ExPolygons> collect_local_z_fixed_state_masks_by_extruder(const std::vector<ExPolygons> &layer_segmentation,
|
||||||
|
const size_t num_physical)
|
||||||
|
{
|
||||||
|
std::vector<ExPolygons> masks_by_extruder(num_physical);
|
||||||
|
for (size_t channel_idx = 1; channel_idx < layer_segmentation.size(); ++channel_idx) {
|
||||||
|
const ExPolygons &state_masks = layer_segmentation[channel_idx];
|
||||||
|
if (state_masks.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const unsigned int state_id = segmentation_channel_filament_id(channel_idx);
|
||||||
|
if (state_id == 0 || state_id > num_physical)
|
||||||
|
continue;
|
||||||
|
append(masks_by_extruder[state_id - 1], state_masks);
|
||||||
|
}
|
||||||
|
for (ExPolygons &masks : masks_by_extruder)
|
||||||
|
if (masks.size() > 1)
|
||||||
|
masks = union_ex(masks);
|
||||||
|
return masks_by_extruder;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<ExPolygons> build_local_z_transition_fixed_masks_for_pass(
|
||||||
|
const std::vector<ExPolygons> ¤t_masks_by_extruder,
|
||||||
|
const std::vector<ExPolygons> &prev_masks_by_extruder,
|
||||||
|
const std::vector<ExPolygons> &next_masks_by_extruder,
|
||||||
|
const size_t pass_idx,
|
||||||
|
const size_t pass_count)
|
||||||
|
{
|
||||||
|
if (pass_count <= 1)
|
||||||
|
return current_masks_by_extruder;
|
||||||
|
|
||||||
|
std::vector<ExPolygons> pass_masks_by_extruder(current_masks_by_extruder.size());
|
||||||
|
const bool is_lowest_pass = pass_idx == 0;
|
||||||
|
const bool is_highest_pass = pass_idx + 1 >= pass_count;
|
||||||
|
|
||||||
|
for (size_t extruder_idx = 0; extruder_idx < current_masks_by_extruder.size(); ++extruder_idx) {
|
||||||
|
const ExPolygons ¤t_masks = current_masks_by_extruder[extruder_idx];
|
||||||
|
if (current_masks.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const ExPolygons prev_masks = extruder_idx < prev_masks_by_extruder.size() ? prev_masks_by_extruder[extruder_idx] : ExPolygons();
|
||||||
|
const ExPolygons next_masks = extruder_idx < next_masks_by_extruder.size() ? next_masks_by_extruder[extruder_idx] : ExPolygons();
|
||||||
|
|
||||||
|
const ExPolygons current_and_prev = prev_masks.empty() ? ExPolygons() : intersection_ex(current_masks, prev_masks);
|
||||||
|
const ExPolygons current_and_next = next_masks.empty() ? ExPolygons() : intersection_ex(current_masks, next_masks);
|
||||||
|
const ExPolygons persistent =
|
||||||
|
current_and_prev.empty() || current_and_next.empty() ? ExPolygons() : intersection_ex(current_and_prev, current_and_next);
|
||||||
|
|
||||||
|
ExPolygons entering = current_and_next;
|
||||||
|
if (!entering.empty() && !current_and_prev.empty())
|
||||||
|
entering = diff_ex(entering, current_and_prev);
|
||||||
|
|
||||||
|
ExPolygons exiting = current_and_prev;
|
||||||
|
if (!exiting.empty() && !current_and_next.empty())
|
||||||
|
exiting = diff_ex(exiting, current_and_next);
|
||||||
|
|
||||||
|
ExPolygons covered;
|
||||||
|
if (!persistent.empty())
|
||||||
|
append(covered, persistent);
|
||||||
|
if (!entering.empty())
|
||||||
|
append(covered, entering);
|
||||||
|
if (!exiting.empty())
|
||||||
|
append(covered, exiting);
|
||||||
|
if (covered.size() > 1)
|
||||||
|
covered = union_ex(covered);
|
||||||
|
|
||||||
|
const ExPolygons isolated = covered.empty() ? current_masks : diff_ex(current_masks, covered);
|
||||||
|
|
||||||
|
ExPolygons assigned;
|
||||||
|
if (!persistent.empty())
|
||||||
|
append(assigned, persistent);
|
||||||
|
if (is_lowest_pass && !exiting.empty())
|
||||||
|
append(assigned, exiting);
|
||||||
|
if (is_highest_pass) {
|
||||||
|
if (!entering.empty())
|
||||||
|
append(assigned, entering);
|
||||||
|
if (!isolated.empty())
|
||||||
|
append(assigned, isolated);
|
||||||
|
}
|
||||||
|
if (assigned.size() > 1)
|
||||||
|
assigned = union_ex(assigned);
|
||||||
|
pass_masks_by_extruder[extruder_idx] = std::move(assigned);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pass_masks_by_extruder;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename ThrowOnCancel>
|
template<typename ThrowOnCancel>
|
||||||
static void build_local_z_plan(PrintObject &print_object, const std::vector<std::vector<ExPolygons>> &segmentation, ThrowOnCancel throw_on_cancel)
|
static void build_local_z_plan(PrintObject &print_object, const std::vector<std::vector<ExPolygons>> &segmentation, ThrowOnCancel throw_on_cancel)
|
||||||
{
|
{
|
||||||
@@ -2744,6 +3039,9 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
const bool local_z_mode = bool_from_full_config(full_cfg, "dithering_local_z_mode", print_cfg.dithering_local_z_mode.value);
|
const bool local_z_mode = bool_from_full_config(full_cfg, "dithering_local_z_mode", print_cfg.dithering_local_z_mode.value);
|
||||||
const bool local_z_whole_objects =
|
const bool local_z_whole_objects =
|
||||||
bool_from_full_config(full_cfg, "dithering_local_z_whole_objects", print_cfg.dithering_local_z_whole_objects.value);
|
bool_from_full_config(full_cfg, "dithering_local_z_whole_objects", print_cfg.dithering_local_z_whole_objects.value);
|
||||||
|
const bool local_z_direct_multicolor =
|
||||||
|
bool_from_full_config(full_cfg, "dithering_local_z_direct_multicolor",
|
||||||
|
print_cfg.dithering_local_z_direct_multicolor.value);
|
||||||
if (!local_z_mode) {
|
if (!local_z_mode) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Local-Z plan skipped: mode disabled"
|
BOOST_LOG_TRIVIAL(debug) << "Local-Z plan skipped: mode disabled"
|
||||||
<< " object=" << object_name;
|
<< " object=" << object_name;
|
||||||
@@ -2771,6 +3069,21 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
|
|
||||||
const MixedFilamentManager &mixed_mgr = print->mixed_filament_manager();
|
const MixedFilamentManager &mixed_mgr = print->mixed_filament_manager();
|
||||||
const auto &mixed_rows = mixed_mgr.mixed_filaments();
|
const auto &mixed_rows = mixed_mgr.mixed_filaments();
|
||||||
|
std::vector<std::vector<unsigned int>> row_direct_component_ids(mixed_rows.size());
|
||||||
|
std::vector<std::vector<int>> row_direct_component_weights(mixed_rows.size());
|
||||||
|
std::vector<std::vector<double>> row_direct_component_error_mm(mixed_rows.size());
|
||||||
|
std::vector<uint8_t> row_uses_direct_multicolor_solver(mixed_rows.size(), uint8_t(0));
|
||||||
|
if (local_z_direct_multicolor && preferred_a <= EPSILON && preferred_b <= EPSILON) {
|
||||||
|
for (size_t row_idx = 0; row_idx < mixed_rows.size(); ++row_idx) {
|
||||||
|
if (local_z_direct_multicolor_row(mixed_rows[row_idx],
|
||||||
|
num_physical,
|
||||||
|
&row_direct_component_ids[row_idx],
|
||||||
|
&row_direct_component_weights[row_idx])) {
|
||||||
|
row_uses_direct_multicolor_solver[row_idx] = uint8_t(1);
|
||||||
|
row_direct_component_error_mm[row_idx].assign(row_direct_component_ids[row_idx].size(), 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
std::vector<uint8_t> pointillism_row_eligible(mixed_rows.size(), uint8_t(0));
|
std::vector<uint8_t> pointillism_row_eligible(mixed_rows.size(), uint8_t(0));
|
||||||
for (size_t row_idx = 0; row_idx < mixed_rows.size(); ++row_idx) {
|
for (size_t row_idx = 0; row_idx < mixed_rows.size(); ++row_idx) {
|
||||||
const std::vector<unsigned int> sequence = pointillism_sequence_for_row(mixed_rows[row_idx], num_physical);
|
const std::vector<unsigned int> sequence = pointillism_sequence_for_row(mixed_rows[row_idx], num_physical);
|
||||||
@@ -2814,6 +3127,8 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
std::vector<std::vector<LocalZActivePair>> row_pair_cycles(mixed_rows.size());
|
std::vector<std::vector<LocalZActivePair>> row_pair_cycles(mixed_rows.size());
|
||||||
std::vector<uint8_t> row_uses_layer_cycle_pair(mixed_rows.size(), uint8_t(0));
|
std::vector<uint8_t> row_uses_layer_cycle_pair(mixed_rows.size(), uint8_t(0));
|
||||||
for (size_t row_idx = 0; row_idx < mixed_rows.size(); ++row_idx) {
|
for (size_t row_idx = 0; row_idx < mixed_rows.size(); ++row_idx) {
|
||||||
|
if (row_uses_direct_multicolor_solver[row_idx] != 0)
|
||||||
|
continue;
|
||||||
row_pair_cycles[row_idx] = build_local_z_pair_cycle_for_row(mixed_rows[row_idx], num_physical);
|
row_pair_cycles[row_idx] = build_local_z_pair_cycle_for_row(mixed_rows[row_idx], num_physical);
|
||||||
if (!row_pair_cycles[row_idx].empty())
|
if (!row_pair_cycles[row_idx].empty())
|
||||||
row_uses_layer_cycle_pair[row_idx] = uint8_t(1);
|
row_uses_layer_cycle_pair[row_idx] = uint8_t(1);
|
||||||
@@ -2826,6 +3141,7 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
<< " mixed_upper=" << mixed_upper
|
<< " mixed_upper=" << mixed_upper
|
||||||
<< " preferred_a=" << preferred_a
|
<< " preferred_a=" << preferred_a
|
||||||
<< " preferred_b=" << preferred_b
|
<< " preferred_b=" << preferred_b
|
||||||
|
<< " direct_multicolor=" << (local_z_direct_multicolor ? 1 : 0)
|
||||||
<< " physical_filaments=" << num_physical;
|
<< " physical_filaments=" << num_physical;
|
||||||
|
|
||||||
std::vector<LocalZInterval> intervals;
|
std::vector<LocalZInterval> intervals;
|
||||||
@@ -2906,6 +3222,10 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
}
|
}
|
||||||
for (size_t row_idx = 0; row_idx < row_active_this_layer.size(); ++row_idx) {
|
for (size_t row_idx = 0; row_idx < row_active_this_layer.size(); ++row_idx) {
|
||||||
if (row_active_this_layer[row_idx] != 0 && row_active_prev_layer[row_idx] == 0) {
|
if (row_active_this_layer[row_idx] != 0 && row_active_prev_layer[row_idx] == 0) {
|
||||||
|
if (row_uses_direct_multicolor_solver[row_idx] != 0 &&
|
||||||
|
row_direct_component_error_mm[row_idx].size() == row_direct_component_ids[row_idx].size()) {
|
||||||
|
std::fill(row_direct_component_error_mm[row_idx].begin(), row_direct_component_error_mm[row_idx].end(), 0.0);
|
||||||
|
}
|
||||||
const bool can_sync_to_dominant =
|
const bool can_sync_to_dominant =
|
||||||
local_z_whole_objects &&
|
local_z_whole_objects &&
|
||||||
dominant_mixed_idx < mixed_rows.size() &&
|
dominant_mixed_idx < mixed_rows.size() &&
|
||||||
@@ -2924,6 +3244,8 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
for (size_t row_idx = 0; row_idx < row_active_this_layer.size(); ++row_idx) {
|
for (size_t row_idx = 0; row_idx < row_active_this_layer.size(); ++row_idx) {
|
||||||
if (row_active_this_layer[row_idx] == 0 || !local_z_eligible_mixed_row(mixed_rows[row_idx]))
|
if (row_active_this_layer[row_idx] == 0 || !local_z_eligible_mixed_row(mixed_rows[row_idx]))
|
||||||
continue;
|
continue;
|
||||||
|
if (row_uses_direct_multicolor_solver[row_idx] != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
const int cadence_index = row_uses_layer_cycle_pair[row_idx] != 0
|
const int cadence_index = row_uses_layer_cycle_pair[row_idx] != 0
|
||||||
? row_layer_cycle_index[row_idx]
|
? row_layer_cycle_index[row_idx]
|
||||||
@@ -2935,9 +3257,11 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
const LocalZActivePair &dominant_pair = row_active_pairs[dominant_mixed_idx];
|
const LocalZActivePair &dominant_pair = row_active_pairs[dominant_mixed_idx];
|
||||||
const int dominant_mix_b_percent =
|
const int dominant_mix_b_percent =
|
||||||
dominant_pair.valid_pair(num_physical) ? dominant_pair.mix_b_percent : mixed_rows[dominant_mixed_idx].mix_b_percent;
|
dominant_pair.valid_pair(num_physical) ? dominant_pair.mix_b_percent : mixed_rows[dominant_mixed_idx].mix_b_percent;
|
||||||
compute_local_z_gradient_component_heights(dominant_mix_b_percent, mixed_lower, mixed_upper,
|
if (row_uses_direct_multicolor_solver[dominant_mixed_idx] == 0) {
|
||||||
dominant_gradient_h_a, dominant_gradient_h_b);
|
compute_local_z_gradient_component_heights(dominant_mix_b_percent, mixed_lower, mixed_upper,
|
||||||
dominant_gradient_valid = true;
|
dominant_gradient_h_a, dominant_gradient_h_b);
|
||||||
|
dominant_gradient_valid = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
total_mixed_state_layers += mixed_state_count;
|
total_mixed_state_layers += mixed_state_count;
|
||||||
if (!mixed_masks.empty())
|
if (!mixed_masks.empty())
|
||||||
@@ -2985,6 +3309,12 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
for (ExPolygons &state_masks : fixed_state_masks_by_extruder)
|
for (ExPolygons &state_masks : fixed_state_masks_by_extruder)
|
||||||
if (state_masks.size() > 1)
|
if (state_masks.size() > 1)
|
||||||
state_masks = union_ex(state_masks);
|
state_masks = union_ex(state_masks);
|
||||||
|
const std::vector<ExPolygons> prev_fixed_state_masks_by_extruder =
|
||||||
|
layer_id > 0 ? collect_local_z_fixed_state_masks_by_extruder(segmentation[layer_id - 1], num_physical)
|
||||||
|
: std::vector<ExPolygons>(num_physical);
|
||||||
|
const std::vector<ExPolygons> next_fixed_state_masks_by_extruder =
|
||||||
|
layer_id + 1 < segmentation.size() ? collect_local_z_fixed_state_masks_by_extruder(segmentation[layer_id + 1], num_physical)
|
||||||
|
: std::vector<ExPolygons>(num_physical);
|
||||||
|
|
||||||
ExPolygons fixed_state_masks_union;
|
ExPolygons fixed_state_masks_union;
|
||||||
for (const ExPolygons &state_masks : fixed_state_masks_by_extruder)
|
for (const ExPolygons &state_masks : fixed_state_masks_by_extruder)
|
||||||
@@ -3004,6 +3334,22 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
if (mixed_masks.size() > 1)
|
if (mixed_masks.size() > 1)
|
||||||
mixed_masks = union_ex(mixed_masks);
|
mixed_masks = union_ex(mixed_masks);
|
||||||
}
|
}
|
||||||
|
if (local_z_whole_objects && !fixed_state_masks_union.empty()) {
|
||||||
|
constexpr double LOCAL_Z_WHOLE_OBJECT_FIXED_GUARD_MM = 0.10;
|
||||||
|
ExPolygons fixed_state_guard_masks = offset_ex(fixed_state_masks_union, float(scale_(LOCAL_Z_WHOLE_OBJECT_FIXED_GUARD_MM)));
|
||||||
|
if (fixed_state_guard_masks.empty())
|
||||||
|
fixed_state_guard_masks = fixed_state_masks_union;
|
||||||
|
else if (fixed_state_guard_masks.size() > 1)
|
||||||
|
fixed_state_guard_masks = union_ex(fixed_state_guard_masks);
|
||||||
|
|
||||||
|
for (ExPolygons &state_masks : row_state_masks) {
|
||||||
|
if (state_masks.empty())
|
||||||
|
continue;
|
||||||
|
state_masks = diff_ex(state_masks, fixed_state_guard_masks);
|
||||||
|
if (state_masks.size() > 1)
|
||||||
|
state_masks = union_ex(state_masks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
size_t active_row_mask_components = 0;
|
size_t active_row_mask_components = 0;
|
||||||
size_t active_row_mask_vertices = 0;
|
size_t active_row_mask_vertices = 0;
|
||||||
@@ -3040,19 +3386,29 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
if (row_active_this_layer[row_idx] == 0)
|
if (row_active_this_layer[row_idx] == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
double row_h_a = 0.0;
|
std::vector<double> row_passes;
|
||||||
double row_h_b = 0.0;
|
if (row_uses_direct_multicolor_solver[row_idx] != 0) {
|
||||||
const LocalZActivePair &active_pair = row_active_pairs[row_idx];
|
row_passes = build_local_z_direct_multicolor_pass_heights(mixed_rows[row_idx],
|
||||||
const int row_mix_b_percent =
|
row_direct_component_weights[row_idx],
|
||||||
active_pair.valid_pair(num_physical) ? active_pair.mix_b_percent : mixed_rows[row_idx].mix_b_percent;
|
interval.base_height,
|
||||||
compute_local_z_gradient_component_heights(row_mix_b_percent, mixed_lower, mixed_upper, row_h_a, row_h_b);
|
mixed_lower,
|
||||||
std::vector<double> row_passes = active_pair.uses_layer_cycle_sequence
|
mixed_upper,
|
||||||
? build_local_z_two_pass_heights(interval.base_height, mixed_lower, mixed_upper, row_h_a, row_h_b)
|
row_direct_component_ids[row_idx].size());
|
||||||
: build_local_z_alternating_pass_heights(interval.base_height,
|
} else {
|
||||||
mixed_lower,
|
double row_h_a = 0.0;
|
||||||
mixed_upper,
|
double row_h_b = 0.0;
|
||||||
row_h_a,
|
const LocalZActivePair &active_pair = row_active_pairs[row_idx];
|
||||||
row_h_b);
|
const int row_mix_b_percent =
|
||||||
|
active_pair.valid_pair(num_physical) ? active_pair.mix_b_percent : mixed_rows[row_idx].mix_b_percent;
|
||||||
|
compute_local_z_gradient_component_heights(row_mix_b_percent, mixed_lower, mixed_upper, row_h_a, row_h_b);
|
||||||
|
row_passes = active_pair.uses_layer_cycle_sequence
|
||||||
|
? build_local_z_two_pass_heights(interval.base_height, mixed_lower, mixed_upper, row_h_a, row_h_b)
|
||||||
|
: build_local_z_alternating_pass_heights(interval.base_height,
|
||||||
|
mixed_lower,
|
||||||
|
mixed_upper,
|
||||||
|
row_h_a,
|
||||||
|
row_h_b);
|
||||||
|
}
|
||||||
if (row_passes.empty())
|
if (row_passes.empty())
|
||||||
row_passes.emplace_back(interval.base_height);
|
row_passes.emplace_back(interval.base_height);
|
||||||
if (!sanitize_local_z_pass_heights(row_passes, interval.base_height, mixed_lower, mixed_upper))
|
if (!sanitize_local_z_pass_heights(row_passes, interval.base_height, mixed_lower, mixed_upper))
|
||||||
@@ -3076,6 +3432,16 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
pass_heights = build_local_z_shared_pass_heights(interval.base_height, mixed_lower, mixed_upper);
|
pass_heights = build_local_z_shared_pass_heights(interval.base_height, mixed_lower, mixed_upper);
|
||||||
if (pass_heights.size() > 1)
|
if (pass_heights.size() > 1)
|
||||||
++alternating_height_intervals;
|
++alternating_height_intervals;
|
||||||
|
} else if (dominant_mixed_idx < mixed_rows.size() &&
|
||||||
|
row_uses_direct_multicolor_solver[dominant_mixed_idx] != 0) {
|
||||||
|
pass_heights = build_local_z_direct_multicolor_pass_heights(mixed_rows[dominant_mixed_idx],
|
||||||
|
row_direct_component_weights[dominant_mixed_idx],
|
||||||
|
interval.base_height,
|
||||||
|
mixed_lower,
|
||||||
|
mixed_upper,
|
||||||
|
row_direct_component_ids[dominant_mixed_idx].size());
|
||||||
|
if (pass_heights.size() > 1)
|
||||||
|
++alternating_height_intervals;
|
||||||
} else if (dominant_gradient_valid) {
|
} else if (dominant_gradient_valid) {
|
||||||
const bool dominant_uses_pair_cycle =
|
const bool dominant_uses_pair_cycle =
|
||||||
dominant_mixed_idx < mixed_rows.size() && row_active_pairs[dominant_mixed_idx].uses_layer_cycle_sequence;
|
dominant_mixed_idx < mixed_rows.size() && row_active_pairs[dominant_mixed_idx].uses_layer_cycle_sequence;
|
||||||
@@ -3144,13 +3510,20 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
? std::vector<double>{ interval.base_height }
|
? std::vector<double>{ interval.base_height }
|
||||||
: row_passes_raw;
|
: row_passes_raw;
|
||||||
const LocalZActivePair &active_pair = row_active_pairs[row_idx];
|
const LocalZActivePair &active_pair = row_active_pairs[row_idx];
|
||||||
|
const bool uses_direct_multicolor = row_uses_direct_multicolor_solver[row_idx] != 0;
|
||||||
const bool valid_pair = active_pair.valid_pair(num_physical);
|
const bool valid_pair = active_pair.valid_pair(num_physical);
|
||||||
const int orientation_cadence_index = active_pair.uses_layer_cycle_sequence
|
const int orientation_cadence_index = active_pair.uses_layer_cycle_sequence
|
||||||
? row_layer_cycle_index[row_idx]
|
? row_layer_cycle_index[row_idx]
|
||||||
: row_cadence_index[row_idx];
|
: row_cadence_index[row_idx];
|
||||||
|
const std::vector<unsigned int> direct_sequence = uses_direct_multicolor
|
||||||
|
? build_local_z_direct_multicolor_sequence(row_direct_component_ids[row_idx],
|
||||||
|
row_direct_component_weights[row_idx],
|
||||||
|
row_passes,
|
||||||
|
row_direct_component_error_mm[row_idx])
|
||||||
|
: std::vector<unsigned int>();
|
||||||
|
|
||||||
bool start_with_a = true;
|
bool start_with_a = true;
|
||||||
if (valid_pair && preferred_a <= EPSILON && preferred_b <= EPSILON) {
|
if (!uses_direct_multicolor && valid_pair && preferred_a <= EPSILON && preferred_b <= EPSILON) {
|
||||||
double row_h_a = 0.0;
|
double row_h_a = 0.0;
|
||||||
double row_h_b = 0.0;
|
double row_h_b = 0.0;
|
||||||
compute_local_z_gradient_component_heights(active_pair.mix_b_percent, mixed_lower, mixed_upper, row_h_a, row_h_b);
|
compute_local_z_gradient_component_heights(active_pair.mix_b_percent, mixed_lower, mixed_upper, row_h_a, row_h_b);
|
||||||
@@ -3180,11 +3553,15 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
plan.print_z = z_next;
|
plan.print_z = z_next;
|
||||||
plan.flow_height = pass_height;
|
plan.flow_height = pass_height;
|
||||||
plan.painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
plan.painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
||||||
|
plan.fixed_painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
||||||
++split_passes_total;
|
++split_passes_total;
|
||||||
++forced_height_resolve_calls;
|
++forced_height_resolve_calls;
|
||||||
|
|
||||||
unsigned int target_extruder = 0;
|
unsigned int target_extruder = 0;
|
||||||
if (valid_pair) {
|
if (uses_direct_multicolor) {
|
||||||
|
if (pass_i < direct_sequence.size())
|
||||||
|
target_extruder = direct_sequence[pass_i];
|
||||||
|
} else if (valid_pair) {
|
||||||
const bool even_pass = (pass_i % 2) == 0;
|
const bool even_pass = (pass_i % 2) == 0;
|
||||||
target_extruder = even_pass
|
target_extruder = even_pass
|
||||||
? (start_with_a ? active_pair.component_a : active_pair.component_b)
|
? (start_with_a ? active_pair.component_a : active_pair.component_b)
|
||||||
@@ -3212,24 +3589,17 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
++split_passes_with_painted_masks;
|
++split_passes_with_painted_masks;
|
||||||
interval_has_split_painted_masks = true;
|
interval_has_split_painted_masks = true;
|
||||||
}
|
}
|
||||||
for (size_t extruder_idx = 0; extruder_idx < fixed_state_masks_by_extruder.size(); ++extruder_idx)
|
|
||||||
if (!fixed_state_masks_by_extruder[extruder_idx].empty())
|
|
||||||
append(plan.painted_masks_by_extruder[extruder_idx], fixed_state_masks_by_extruder[extruder_idx]);
|
|
||||||
if (!fixed_state_masks_union.empty()) {
|
|
||||||
++split_passes_with_painted_masks;
|
|
||||||
interval_has_split_painted_masks = true;
|
|
||||||
}
|
|
||||||
for (ExPolygons &masks : plan.painted_masks_by_extruder)
|
for (ExPolygons &masks : plan.painted_masks_by_extruder)
|
||||||
if (masks.size() > 1)
|
if (masks.size() > 1)
|
||||||
masks = union_ex(masks);
|
masks = union_ex(masks);
|
||||||
|
|
||||||
isolated_plans.emplace_back(std::move(plan));
|
isolated_plans.emplace_back(std::move(plan));
|
||||||
row_used = true;
|
row_used = true;
|
||||||
if (!active_pair.uses_layer_cycle_sequence)
|
if (!uses_direct_multicolor && !active_pair.uses_layer_cycle_sequence)
|
||||||
++row_cadence_index[row_idx];
|
++row_cadence_index[row_idx];
|
||||||
z_cursor = z_next;
|
z_cursor = z_next;
|
||||||
}
|
}
|
||||||
if (row_used && active_pair.uses_layer_cycle_sequence)
|
if (row_used && !uses_direct_multicolor && active_pair.uses_layer_cycle_sequence)
|
||||||
++row_layer_cycle_index[row_idx];
|
++row_layer_cycle_index[row_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3247,6 +3617,31 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
isolated_plans[idx].pass_index = idx;
|
isolated_plans[idx].pass_index = idx;
|
||||||
min_flow_height = std::min(min_flow_height, isolated_plans[idx].flow_height);
|
min_flow_height = std::min(min_flow_height, isolated_plans[idx].flow_height);
|
||||||
max_flow_height = std::max(max_flow_height, isolated_plans[idx].flow_height);
|
max_flow_height = std::max(max_flow_height, isolated_plans[idx].flow_height);
|
||||||
|
const std::vector<ExPolygons> fixed_masks_for_pass =
|
||||||
|
build_local_z_transition_fixed_masks_for_pass(fixed_state_masks_by_extruder,
|
||||||
|
prev_fixed_state_masks_by_extruder,
|
||||||
|
next_fixed_state_masks_by_extruder,
|
||||||
|
idx,
|
||||||
|
isolated_plans.size());
|
||||||
|
bool plan_has_fixed_masks = false;
|
||||||
|
for (size_t extruder_idx = 0; extruder_idx < fixed_masks_for_pass.size() &&
|
||||||
|
extruder_idx < isolated_plans[idx].fixed_painted_masks_by_extruder.size();
|
||||||
|
++extruder_idx) {
|
||||||
|
if (fixed_masks_for_pass[extruder_idx].empty())
|
||||||
|
continue;
|
||||||
|
append(isolated_plans[idx].fixed_painted_masks_by_extruder[extruder_idx], fixed_masks_for_pass[extruder_idx]);
|
||||||
|
plan_has_fixed_masks = true;
|
||||||
|
}
|
||||||
|
for (ExPolygons &masks : isolated_plans[idx].painted_masks_by_extruder)
|
||||||
|
if (masks.size() > 1)
|
||||||
|
masks = union_ex(masks);
|
||||||
|
for (ExPolygons &masks : isolated_plans[idx].fixed_painted_masks_by_extruder)
|
||||||
|
if (masks.size() > 1)
|
||||||
|
masks = union_ex(masks);
|
||||||
|
if (plan_has_fixed_masks) {
|
||||||
|
++split_passes_with_painted_masks;
|
||||||
|
interval_has_split_painted_masks = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isolated_plans.back().base_masks = base_masks;
|
isolated_plans.back().base_masks = base_masks;
|
||||||
interval.sublayer_height = min_flow_height;
|
interval.sublayer_height = min_flow_height;
|
||||||
@@ -3263,10 +3658,19 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
// Derive per-row orientation against pass heights so each mixed row
|
// Derive per-row orientation against pass heights so each mixed row
|
||||||
// maps thicker/thinner subpasses to the intended component.
|
// maps thicker/thinner subpasses to the intended component.
|
||||||
std::vector<uint8_t> start_with_component_a(mixed_rows.size(), uint8_t(1));
|
std::vector<uint8_t> start_with_component_a(mixed_rows.size(), uint8_t(1));
|
||||||
|
std::vector<std::vector<unsigned int>> row_direct_pass_sequences(mixed_rows.size());
|
||||||
if (preferred_a <= EPSILON && preferred_b <= EPSILON) {
|
if (preferred_a <= EPSILON && preferred_b <= EPSILON) {
|
||||||
for (size_t row_idx = 0; row_idx < row_active_this_layer.size(); ++row_idx) {
|
for (size_t row_idx = 0; row_idx < row_active_this_layer.size(); ++row_idx) {
|
||||||
if (row_active_this_layer[row_idx] == 0 || !local_z_eligible_mixed_row(mixed_rows[row_idx]))
|
if (row_active_this_layer[row_idx] == 0 || !local_z_eligible_mixed_row(mixed_rows[row_idx]))
|
||||||
continue;
|
continue;
|
||||||
|
if (row_uses_direct_multicolor_solver[row_idx] != 0) {
|
||||||
|
row_direct_pass_sequences[row_idx] =
|
||||||
|
build_local_z_direct_multicolor_sequence(row_direct_component_ids[row_idx],
|
||||||
|
row_direct_component_weights[row_idx],
|
||||||
|
pass_heights,
|
||||||
|
row_direct_component_error_mm[row_idx]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const LocalZActivePair &active_pair = row_active_pairs[row_idx];
|
const LocalZActivePair &active_pair = row_active_pairs[row_idx];
|
||||||
if (!active_pair.valid_pair(num_physical))
|
if (!active_pair.valid_pair(num_physical))
|
||||||
@@ -3305,32 +3709,33 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
plan.print_z = z_next;
|
plan.print_z = z_next;
|
||||||
plan.flow_height = pass_height;
|
plan.flow_height = pass_height;
|
||||||
plan.painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
plan.painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
||||||
|
plan.fixed_painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
||||||
++split_passes_total;
|
++split_passes_total;
|
||||||
bool pass_has_painted_masks = false;
|
bool pass_has_painted_masks = false;
|
||||||
std::vector<uint8_t> row_seen_in_pass(mixed_rows.size(), uint8_t(0));
|
std::vector<uint8_t> row_seen_in_pass(mixed_rows.size(), uint8_t(0));
|
||||||
|
|
||||||
for (size_t channel_idx = 0; channel_idx < segmentation[layer_id].size(); ++channel_idx) {
|
for (size_t row_idx = 0; row_idx < row_state_masks.size(); ++row_idx) {
|
||||||
const ExPolygons &state_masks = segmentation[layer_id][channel_idx];
|
const ExPolygons &state_masks = row_state_masks[row_idx];
|
||||||
if (state_masks.empty())
|
if (state_masks.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const unsigned int state_id = segmentation_channel_filament_id(channel_idx);
|
const unsigned int state_id = row_state_ids[row_idx];
|
||||||
if (!mixed_mgr.is_mixed(state_id, num_physical))
|
if (state_id == 0)
|
||||||
continue;
|
continue;
|
||||||
const int mixed_idx = mixed_mgr.mixed_index_from_filament_id(state_id, num_physical);
|
|
||||||
if (mixed_idx < 0 || size_t(mixed_idx) >= mixed_rows.size())
|
|
||||||
continue;
|
|
||||||
const size_t row_idx = size_t(mixed_idx);
|
|
||||||
const MixedFilament &mf = mixed_rows[row_idx];
|
const MixedFilament &mf = mixed_rows[row_idx];
|
||||||
if (!local_z_eligible_mixed_row(mf))
|
if (!local_z_eligible_mixed_row(mf))
|
||||||
continue;
|
continue;
|
||||||
const LocalZActivePair &active_pair = row_active_pairs[row_idx];
|
const LocalZActivePair &active_pair = row_active_pairs[row_idx];
|
||||||
|
const bool uses_direct_multicolor = row_uses_direct_multicolor_solver[row_idx] != 0;
|
||||||
row_seen_in_pass[row_idx] = uint8_t(1);
|
row_seen_in_pass[row_idx] = uint8_t(1);
|
||||||
if (active_pair.uses_layer_cycle_sequence)
|
if (!uses_direct_multicolor && active_pair.uses_layer_cycle_sequence)
|
||||||
row_seen_sequence_in_interval[row_idx] = uint8_t(1);
|
row_seen_sequence_in_interval[row_idx] = uint8_t(1);
|
||||||
++forced_height_resolve_calls;
|
++forced_height_resolve_calls;
|
||||||
unsigned int target_extruder = 0;
|
unsigned int target_extruder = 0;
|
||||||
if (active_pair.valid_pair(num_physical)) {
|
if (uses_direct_multicolor) {
|
||||||
|
if (pass_idx < row_direct_pass_sequences[row_idx].size())
|
||||||
|
target_extruder = row_direct_pass_sequences[row_idx][pass_idx];
|
||||||
|
} else if (active_pair.valid_pair(num_physical)) {
|
||||||
const bool start_a = start_with_component_a[row_idx] != 0;
|
const bool start_a = start_with_component_a[row_idx] != 0;
|
||||||
const bool even_pass = (pass_idx % 2) == 0;
|
const bool even_pass = (pass_idx % 2) == 0;
|
||||||
// Local-Z mode alternates A/B on every subpass.
|
// Local-Z mode alternates A/B on every subpass.
|
||||||
@@ -3357,14 +3762,23 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
append(plan.painted_masks_by_extruder[target_extruder - 1], state_masks);
|
append(plan.painted_masks_by_extruder[target_extruder - 1], state_masks);
|
||||||
pass_has_painted_masks = true;
|
pass_has_painted_masks = true;
|
||||||
}
|
}
|
||||||
for (size_t extruder_idx = 0; extruder_idx < fixed_state_masks_by_extruder.size(); ++extruder_idx)
|
const std::vector<ExPolygons> fixed_masks_for_pass =
|
||||||
if (!fixed_state_masks_by_extruder[extruder_idx].empty()) {
|
build_local_z_transition_fixed_masks_for_pass(fixed_state_masks_by_extruder,
|
||||||
append(plan.painted_masks_by_extruder[extruder_idx], fixed_state_masks_by_extruder[extruder_idx]);
|
prev_fixed_state_masks_by_extruder,
|
||||||
|
next_fixed_state_masks_by_extruder,
|
||||||
|
pass_idx,
|
||||||
|
pass_heights.size());
|
||||||
|
for (size_t extruder_idx = 0; extruder_idx < fixed_masks_for_pass.size(); ++extruder_idx)
|
||||||
|
if (!fixed_masks_for_pass[extruder_idx].empty()) {
|
||||||
|
append(plan.fixed_painted_masks_by_extruder[extruder_idx], fixed_masks_for_pass[extruder_idx]);
|
||||||
pass_has_painted_masks = true;
|
pass_has_painted_masks = true;
|
||||||
}
|
}
|
||||||
for (ExPolygons &masks : plan.painted_masks_by_extruder)
|
for (ExPolygons &masks : plan.painted_masks_by_extruder)
|
||||||
if (masks.size() > 1)
|
if (masks.size() > 1)
|
||||||
masks = union_ex(masks);
|
masks = union_ex(masks);
|
||||||
|
for (ExPolygons &masks : plan.fixed_painted_masks_by_extruder)
|
||||||
|
if (masks.size() > 1)
|
||||||
|
masks = union_ex(masks);
|
||||||
if (pass_has_painted_masks) {
|
if (pass_has_painted_masks) {
|
||||||
++split_passes_with_painted_masks;
|
++split_passes_with_painted_masks;
|
||||||
interval_has_split_painted_masks = true;
|
interval_has_split_painted_masks = true;
|
||||||
@@ -3378,12 +3792,15 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
++total_generated_sublayer_cnt;
|
++total_generated_sublayer_cnt;
|
||||||
++pass_idx;
|
++pass_idx;
|
||||||
for (size_t mixed_idx = 0; mixed_idx < row_seen_in_pass.size(); ++mixed_idx)
|
for (size_t mixed_idx = 0; mixed_idx < row_seen_in_pass.size(); ++mixed_idx)
|
||||||
if (row_seen_in_pass[mixed_idx] != 0 && row_uses_layer_cycle_pair[mixed_idx] == 0)
|
if (row_seen_in_pass[mixed_idx] != 0 &&
|
||||||
|
row_uses_layer_cycle_pair[mixed_idx] == 0 &&
|
||||||
|
row_uses_direct_multicolor_solver[mixed_idx] == 0)
|
||||||
++row_cadence_index[mixed_idx];
|
++row_cadence_index[mixed_idx];
|
||||||
z_cursor = z_next;
|
z_cursor = z_next;
|
||||||
}
|
}
|
||||||
for (size_t row_idx = 0; row_idx < row_seen_sequence_in_interval.size(); ++row_idx)
|
for (size_t row_idx = 0; row_idx < row_seen_sequence_in_interval.size(); ++row_idx)
|
||||||
if (row_seen_sequence_in_interval[row_idx] != 0)
|
if (row_seen_sequence_in_interval[row_idx] != 0 &&
|
||||||
|
row_uses_direct_multicolor_solver[row_idx] == 0)
|
||||||
++row_layer_cycle_index[row_idx];
|
++row_layer_cycle_index[row_idx];
|
||||||
}
|
}
|
||||||
if (!interval_has_split_painted_masks)
|
if (!interval_has_split_painted_masks)
|
||||||
@@ -3401,35 +3818,43 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
plan.flow_height = interval.base_height;
|
plan.flow_height = interval.base_height;
|
||||||
plan.base_masks = base_masks;
|
plan.base_masks = base_masks;
|
||||||
plan.painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
plan.painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
||||||
|
plan.fixed_painted_masks_by_extruder.assign(num_physical, ExPolygons());
|
||||||
std::vector<uint8_t> row_seen_in_interval(mixed_rows.size(), uint8_t(0));
|
std::vector<uint8_t> row_seen_in_interval(mixed_rows.size(), uint8_t(0));
|
||||||
|
|
||||||
for (size_t channel_idx = 0; channel_idx < segmentation[layer_id].size(); ++channel_idx) {
|
for (size_t row_idx = 0; row_idx < row_state_masks.size(); ++row_idx) {
|
||||||
const ExPolygons &state_masks = segmentation[layer_id][channel_idx];
|
const ExPolygons &state_masks = row_state_masks[row_idx];
|
||||||
if (state_masks.empty())
|
if (state_masks.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const unsigned int state_id = segmentation_channel_filament_id(channel_idx);
|
const unsigned int state_id = row_state_ids[row_idx];
|
||||||
if (!mixed_mgr.is_mixed(state_id, num_physical))
|
if (state_id == 0)
|
||||||
continue;
|
continue;
|
||||||
const int mixed_idx = mixed_mgr.mixed_index_from_filament_id(state_id, num_physical);
|
|
||||||
if (mixed_idx < 0 || size_t(mixed_idx) >= mixed_rows.size())
|
|
||||||
continue;
|
|
||||||
const size_t row_idx = size_t(mixed_idx);
|
|
||||||
const MixedFilament &mixed_row = mixed_rows[row_idx];
|
const MixedFilament &mixed_row = mixed_rows[row_idx];
|
||||||
if (!local_z_eligible_mixed_row(mixed_row))
|
if (!local_z_eligible_mixed_row(mixed_row))
|
||||||
continue;
|
continue;
|
||||||
row_seen_in_interval[row_idx] = uint8_t(1);
|
row_seen_in_interval[row_idx] = uint8_t(1);
|
||||||
++forced_height_resolve_calls;
|
++forced_height_resolve_calls;
|
||||||
const int resolve_cadence_index = row_uses_layer_cycle_pair[row_idx] != 0
|
unsigned int target_extruder = 0;
|
||||||
? row_layer_cycle_index[row_idx]
|
if (row_uses_direct_multicolor_solver[row_idx] != 0) {
|
||||||
: row_cadence_index[row_idx];
|
const std::vector<unsigned int> direct_sequence =
|
||||||
const unsigned int target_extruder =
|
build_local_z_direct_multicolor_sequence(row_direct_component_ids[row_idx],
|
||||||
mixed_mgr.resolve(state_id,
|
row_direct_component_weights[row_idx],
|
||||||
num_physical,
|
std::vector<double>{ interval.base_height },
|
||||||
resolve_cadence_index,
|
row_direct_component_error_mm[row_idx]);
|
||||||
float(plan.print_z),
|
if (!direct_sequence.empty())
|
||||||
float(plan.flow_height),
|
target_extruder = direct_sequence.front();
|
||||||
force_height_resolve);
|
} else {
|
||||||
|
const int resolve_cadence_index = row_uses_layer_cycle_pair[row_idx] != 0
|
||||||
|
? row_layer_cycle_index[row_idx]
|
||||||
|
: row_cadence_index[row_idx];
|
||||||
|
target_extruder =
|
||||||
|
mixed_mgr.resolve(state_id,
|
||||||
|
num_physical,
|
||||||
|
resolve_cadence_index,
|
||||||
|
float(plan.print_z),
|
||||||
|
float(plan.flow_height),
|
||||||
|
force_height_resolve);
|
||||||
|
}
|
||||||
if (target_extruder == 0 || target_extruder > num_physical) {
|
if (target_extruder == 0 || target_extruder > num_physical) {
|
||||||
++forced_height_resolve_invalid_target;
|
++forced_height_resolve_invalid_target;
|
||||||
continue;
|
continue;
|
||||||
@@ -3438,17 +3863,21 @@ static void build_local_z_plan(PrintObject &print_object, const std::vector<std:
|
|||||||
}
|
}
|
||||||
for (size_t extruder_idx = 0; extruder_idx < fixed_state_masks_by_extruder.size(); ++extruder_idx)
|
for (size_t extruder_idx = 0; extruder_idx < fixed_state_masks_by_extruder.size(); ++extruder_idx)
|
||||||
if (!fixed_state_masks_by_extruder[extruder_idx].empty())
|
if (!fixed_state_masks_by_extruder[extruder_idx].empty())
|
||||||
append(plan.painted_masks_by_extruder[extruder_idx], fixed_state_masks_by_extruder[extruder_idx]);
|
append(plan.fixed_painted_masks_by_extruder[extruder_idx], fixed_state_masks_by_extruder[extruder_idx]);
|
||||||
for (ExPolygons &masks : plan.painted_masks_by_extruder)
|
for (ExPolygons &masks : plan.painted_masks_by_extruder)
|
||||||
if (masks.size() > 1)
|
if (masks.size() > 1)
|
||||||
masks = union_ex(masks);
|
masks = union_ex(masks);
|
||||||
|
for (ExPolygons &masks : plan.fixed_painted_masks_by_extruder)
|
||||||
|
if (masks.size() > 1)
|
||||||
|
masks = union_ex(masks);
|
||||||
|
|
||||||
plans.emplace_back(std::move(plan));
|
plans.emplace_back(std::move(plan));
|
||||||
interval.sublayer_count = 1;
|
interval.sublayer_count = 1;
|
||||||
++total_generated_sublayer_cnt;
|
++total_generated_sublayer_cnt;
|
||||||
for (size_t mixed_idx = 0; mixed_idx < row_seen_in_interval.size(); ++mixed_idx)
|
for (size_t mixed_idx = 0; mixed_idx < row_seen_in_interval.size(); ++mixed_idx)
|
||||||
if (row_seen_in_interval[mixed_idx] != 0)
|
if (row_seen_in_interval[mixed_idx] != 0)
|
||||||
(row_uses_layer_cycle_pair[mixed_idx] != 0 ? row_layer_cycle_index[mixed_idx] : row_cadence_index[mixed_idx])++;
|
if (row_uses_direct_multicolor_solver[mixed_idx] == 0)
|
||||||
|
(row_uses_layer_cycle_pair[mixed_idx] != 0 ? row_layer_cycle_index[mixed_idx] : row_cadence_index[mixed_idx])++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interval.has_mixed_paint) {
|
if (interval.has_mixed_paint) {
|
||||||
|
|||||||
@@ -785,6 +785,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
|||||||
config->has("dithering_local_z_mode") && config->option("dithering_local_z_mode") != nullptr &&
|
config->has("dithering_local_z_mode") && config->option("dithering_local_z_mode") != nullptr &&
|
||||||
config->opt_bool("dithering_local_z_mode");
|
config->opt_bool("dithering_local_z_mode");
|
||||||
toggle_line("dithering_local_z_whole_objects", local_z_dithering_enabled);
|
toggle_line("dithering_local_z_whole_objects", local_z_dithering_enabled);
|
||||||
|
toggle_line("dithering_local_z_direct_multicolor", local_z_dithering_enabled);
|
||||||
toggle_line("local_z_wipe_tower_purge_lines", have_prime_tower && !is_BBL_Printer && local_z_dithering_enabled);
|
toggle_line("local_z_wipe_tower_purge_lines", have_prime_tower && !is_BBL_Printer && local_z_dithering_enabled);
|
||||||
|
|
||||||
WipeTowerWallType wipe_tower_wall_type = config->opt_enum<WipeTowerWallType>("wipe_tower_wall_type");
|
WipeTowerWallType wipe_tower_wall_type = config->opt_enum<WipeTowerWallType>("wipe_tower_wall_type");
|
||||||
|
|||||||
@@ -6412,6 +6412,7 @@ void GUI_App::load_current_presets(bool active_preset_combox/*= false*/, bool ch
|
|||||||
"dithering_z_step_size",
|
"dithering_z_step_size",
|
||||||
"dithering_local_z_mode",
|
"dithering_local_z_mode",
|
||||||
"dithering_local_z_whole_objects",
|
"dithering_local_z_whole_objects",
|
||||||
|
"dithering_local_z_direct_multicolor",
|
||||||
"dithering_step_painted_zones_only",
|
"dithering_step_painted_zones_only",
|
||||||
"mixed_filament_pointillism_pixel_size",
|
"mixed_filament_pointillism_pixel_size",
|
||||||
"mixed_filament_pointillism_line_gap",
|
"mixed_filament_pointillism_line_gap",
|
||||||
|
|||||||
@@ -5671,6 +5671,10 @@ MixedFilamentDisplayContext build_mixed_filament_display_context(const std::vect
|
|||||||
if (print_cfg != nullptr && print_cfg->has("wall_loops"))
|
if (print_cfg != nullptr && print_cfg->has("wall_loops"))
|
||||||
context.preview_settings.wall_loops = std::max<size_t>(1, size_t(std::max(1, print_cfg->opt_int("wall_loops"))));
|
context.preview_settings.wall_loops = std::max<size_t>(1, size_t(std::max(1, print_cfg->opt_int("wall_loops"))));
|
||||||
context.preview_settings.local_z_mode = get_mixed_bool("dithering_local_z_mode", false);
|
context.preview_settings.local_z_mode = get_mixed_bool("dithering_local_z_mode", false);
|
||||||
|
context.preview_settings.local_z_direct_multicolor =
|
||||||
|
get_mixed_bool("dithering_local_z_direct_multicolor", false) &&
|
||||||
|
context.preview_settings.preferred_a_height <= EPSILON &&
|
||||||
|
context.preview_settings.preferred_b_height <= EPSILON;
|
||||||
context.component_bias_enabled = get_mixed_bool("mixed_filament_component_bias_enabled", false);
|
context.component_bias_enabled = get_mixed_bool("mixed_filament_component_bias_enabled", false);
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
@@ -5866,6 +5870,26 @@ std::string MixedFilamentConfigPanel::summarize_local_z_breakdown(const MixedFil
|
|||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<unsigned int> ids = decode_gradient_ids(mf.gradient_component_ids);
|
const std::vector<unsigned int> ids = decode_gradient_ids(mf.gradient_component_ids);
|
||||||
|
if (preview_settings.local_z_mode && preview_settings.local_z_direct_multicolor && ids.size() >= 3) {
|
||||||
|
const std::vector<int> normalized = normalize_gradient_weights(weights, ids.size());
|
||||||
|
const size_t effective_sublayers =
|
||||||
|
mf.local_z_max_sublayers >= 2 ? size_t(std::max(2, mf.local_z_max_sublayers)) : ids.size();
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "Local-Z direct multicolor solver: ";
|
||||||
|
for (size_t idx = 0; idx < ids.size(); ++idx) {
|
||||||
|
if (idx > 0)
|
||||||
|
ss << ", ";
|
||||||
|
const int pct = idx < normalized.size() ? normalized[idx] : 0;
|
||||||
|
ss << 'F' << ids[idx] << ' ' << pct << '%';
|
||||||
|
}
|
||||||
|
ss << ".\nCarry-over error is distributed directly across all " << ids.size()
|
||||||
|
<< " components instead of collapsing them into pair cadence.";
|
||||||
|
if (mf.local_z_max_sublayers >= 2)
|
||||||
|
ss << "\nEffective Local-Z cap: up to " << effective_sublayers << " sublayers per nominal layer.";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
if (ids.size() >= 4) {
|
if (ids.size() >= 4) {
|
||||||
const std::vector<int> normalized = normalize_gradient_weights(weights, ids.size());
|
const std::vector<int> normalized = normalize_gradient_weights(weights, ids.size());
|
||||||
const std::vector<unsigned int> pair_tokens = { 1, 2 };
|
const std::vector<unsigned int> pair_tokens = { 1, 2 };
|
||||||
@@ -7281,6 +7305,10 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
|
|||||||
if (print_cfg && print_cfg->has("wall_loops"))
|
if (print_cfg && print_cfg->has("wall_loops"))
|
||||||
wall_loops = std::max<size_t>(1, size_t(std::max(1, print_cfg->opt_int("wall_loops"))));
|
wall_loops = std::max<size_t>(1, size_t(std::max(1, print_cfg->opt_int("wall_loops"))));
|
||||||
const bool local_z_mode = get_mixed_bool("dithering_local_z_mode", false);
|
const bool local_z_mode = get_mixed_bool("dithering_local_z_mode", false);
|
||||||
|
const bool local_z_direct_multicolor =
|
||||||
|
get_mixed_bool("dithering_local_z_direct_multicolor", false) &&
|
||||||
|
preferred_local_z_a <= EPSILON &&
|
||||||
|
preferred_local_z_b <= EPSILON;
|
||||||
const bool component_bias_enabled = get_mixed_bool("mixed_filament_component_bias_enabled", false);
|
const bool component_bias_enabled = get_mixed_bool("mixed_filament_component_bias_enabled", false);
|
||||||
float pointillism_pixel_size = std::max(0.f, get_mixed_float("mixed_filament_pointillism_pixel_size", 0.f));
|
float pointillism_pixel_size = std::max(0.f, get_mixed_float("mixed_filament_pointillism_pixel_size", 0.f));
|
||||||
float pointillism_line_gap = std::max(0.f, get_mixed_float("mixed_filament_pointillism_line_gap", 0.f));
|
float pointillism_line_gap = std::max(0.f, get_mixed_float("mixed_filament_pointillism_line_gap", 0.f));
|
||||||
@@ -7294,6 +7322,7 @@ void Sidebar::update_mixed_filament_panel(bool sync_manager)
|
|||||||
preferred_local_z_a,
|
preferred_local_z_a,
|
||||||
preferred_local_z_b,
|
preferred_local_z_b,
|
||||||
local_z_mode,
|
local_z_mode,
|
||||||
|
local_z_direct_multicolor,
|
||||||
wall_loops
|
wall_loops
|
||||||
};
|
};
|
||||||
const MixedFilamentDisplayContext display_context {
|
const MixedFilamentDisplayContext display_context {
|
||||||
@@ -10493,6 +10522,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
"dithering_z_step_size",
|
"dithering_z_step_size",
|
||||||
"dithering_local_z_mode",
|
"dithering_local_z_mode",
|
||||||
"dithering_local_z_whole_objects",
|
"dithering_local_z_whole_objects",
|
||||||
|
"dithering_local_z_direct_multicolor",
|
||||||
"dithering_step_painted_zones_only"
|
"dithering_step_painted_zones_only"
|
||||||
};
|
};
|
||||||
preset_bundle->project_config.apply_only(config_loaded, imported_project_option_keys, true);
|
preset_bundle->project_config.apply_only(config_loaded, imported_project_option_keys, true);
|
||||||
|
|||||||
@@ -1519,6 +1519,19 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
|||||||
field->set_value(boost::any(false), false);
|
field->set_value(boost::any(false), false);
|
||||||
update_dirty();
|
update_dirty();
|
||||||
}
|
}
|
||||||
|
if (!local_z_enabled &&
|
||||||
|
m_config->has("dithering_local_z_direct_multicolor") &&
|
||||||
|
m_config->option("dithering_local_z_direct_multicolor") != nullptr &&
|
||||||
|
m_config->opt_bool("dithering_local_z_direct_multicolor")) {
|
||||||
|
change_opt_value(*m_config, "dithering_local_z_direct_multicolor", boost::any(false));
|
||||||
|
if (m_type == Preset::TYPE_PRINT) {
|
||||||
|
DynamicPrintConfig &project_cfg = wxGetApp().preset_bundle->project_config;
|
||||||
|
project_cfg.set_key_value("dithering_local_z_direct_multicolor", new ConfigOptionBool(false));
|
||||||
|
}
|
||||||
|
if (Field *field = this->get_field("dithering_local_z_direct_multicolor"))
|
||||||
|
field->set_value(boost::any(false), false);
|
||||||
|
update_dirty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1829,6 +1842,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
|||||||
opt_key == "dithering_z_step_size" ||
|
opt_key == "dithering_z_step_size" ||
|
||||||
opt_key == "dithering_local_z_mode" ||
|
opt_key == "dithering_local_z_mode" ||
|
||||||
opt_key == "dithering_local_z_whole_objects" ||
|
opt_key == "dithering_local_z_whole_objects" ||
|
||||||
|
opt_key == "dithering_local_z_direct_multicolor" ||
|
||||||
opt_key == "dithering_step_painted_zones_only" ||
|
opt_key == "dithering_step_painted_zones_only" ||
|
||||||
opt_key == "mixed_filament_definitions")) {
|
opt_key == "mixed_filament_definitions")) {
|
||||||
DynamicPrintConfig &project_cfg = wxGetApp().preset_bundle->project_config;
|
DynamicPrintConfig &project_cfg = wxGetApp().preset_bundle->project_config;
|
||||||
@@ -2574,6 +2588,7 @@ optgroup->append_single_option_line("skirt_loops", "others_settings_skirt#loops"
|
|||||||
optgroup->append_single_option_line("dithering_z_step_size");
|
optgroup->append_single_option_line("dithering_z_step_size");
|
||||||
optgroup->append_single_option_line("dithering_local_z_mode");
|
optgroup->append_single_option_line("dithering_local_z_mode");
|
||||||
optgroup->append_single_option_line("dithering_local_z_whole_objects");
|
optgroup->append_single_option_line("dithering_local_z_whole_objects");
|
||||||
|
optgroup->append_single_option_line("dithering_local_z_direct_multicolor");
|
||||||
optgroup->append_single_option_line("dithering_step_painted_zones_only");
|
optgroup->append_single_option_line("dithering_step_painted_zones_only");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Fuzzy Skin"), L"fuzzy_skin");
|
optgroup = page->new_optgroup(L("Fuzzy Skin"), L"fuzzy_skin");
|
||||||
|
|||||||
+2
-2
@@ -10,8 +10,8 @@ endif()
|
|||||||
if(NOT DEFINED BBL_INTERNAL_TESTING)
|
if(NOT DEFINED BBL_INTERNAL_TESTING)
|
||||||
set(BBL_INTERNAL_TESTING "0")
|
set(BBL_INTERNAL_TESTING "0")
|
||||||
endif()
|
endif()
|
||||||
set(Snapmaker_VERSION "0.9.7")
|
set(Snapmaker_VERSION "0.9.8")
|
||||||
set(FULLSPECTRUM_VERSION "0.9.7")
|
set(FULLSPECTRUM_VERSION "0.9.8")
|
||||||
set(MIN_FIRM_VER "1.0.0")
|
set(MIN_FIRM_VER "1.0.0")
|
||||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)"
|
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)"
|
||||||
Snapmaker_VERSION_MATCH ${Snapmaker_VERSION})
|
Snapmaker_VERSION_MATCH ${Snapmaker_VERSION})
|
||||||
|
|||||||
Reference in New Issue
Block a user