mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-14 09:02:06 +00:00
Integrations up to commit b3c41fda4180a2946812726218d0a849be0aedb6.
- libslic3r: vendor FilamentMixer; MixedFilamentManager (auto-gen, resolve,
serialize; manual-pattern / gradient / pointillism); 19 new PrintConfig
keys; PresetBundle owns the canonical manager with 3MF + AppConfig
roundtrip and AMS-safe strip+restore; Print owns the slicing-time copy
with PrintApply auto-regen on color change; TriangleSelector::
shift_states_above + filament-id remap; inset_idx propagation through
ExtrusionPath/Loop/MultiPath copy/assign.
- Slicing: virtual filament IDs in painted regions (same-physical channels
collapse when mixed_filament_region_collapse is on); ByObject
collect_filament_data expands mixed slots; pair-cadence + whole-object +
3+component Local-Z plan generators; LocalZOrderOptimizer utility.
- GCode + ToolOrdering: LayerTools resolves virtual IDs through wall /
infill / sparse / solid queries; SameLayerPointillisme in process_layer
(uniform-segment + grouped per-perimeter-index splitters); WipeTower2
Local-Z reservation + sub-layer G-code emission; per-layer infill
filament override.
- PartPlate: get_extruders* expand virtual slots into physical components;
CLI path rebuilds a local manager from full_config.
- GUI: five widget files extracted from FS Plater.cpp (~5000 LOC) —
MixedMixPreview, MixedGradientSelector + WeightsDialog,
MixedFilamentColorMapPanel, MixedFilamentColorMatchDialog (ΔE₀₀ recipe
search), MixedFilamentConfigPanel; Sidebar Mixed Filaments panel
(drag-reorder, enable/delete, Add Gradient/Pattern/Color); Tab exposure
of mixed-filament / dithering / per-layer infill-override settings +
ConfigManipulation visibility and slot-validation rules;
BBLMixedFilamentBroken / BBLSingleExtruderMixedFilamentRisk
notifications + slice gate; WipeTowerDialog edits physical P×P
sub-matrix; bounds-safe extruder_id guards in 3DScene / GLCanvas3D /
GLGizmoMmuSegmentation; change_filament merge guard and
on_filaments_delete is_mixed_before_delete propagation.
- Tests: 4 Catch2 tests for 3MF roundtrip (auto/custom persistence,
PresetBundle string path, total_filaments stability); full-pipeline
slice E2E deferred — TODO in file.
Co-authored-by: Rad <radugheorghiu96@gmail.com>
Co-authored-by: Justin Hayes <justinh@rahb.ca>
Co-authored-by: Calogero Guagenti <calogeroguagenti@gmail.com>
Co-authored-by: xSil3nt <ahmedshazin21@gmail.com>
Co-authored-by: ratdoux <62392831+ratdoux@users.noreply.github.com>
Co-authored-by: Rad <radugheorghiu96@gmail.com>
Co-authored-by: Justin Hayes <justinh@rahb.ca>
Co-authored-by: Calogero Guagenti <calogeroguagenti@gmail.com>
Co-authored-by: xSil3nt <ahmedshazin21@gmail.com>
Co-authored-by: ratdoux <62392831+ratdoux@users.noreply.github.com>
133 lines
5.4 KiB
C++
133 lines
5.4 KiB
C++
#include <catch2/catch_all.hpp>
|
|
|
|
#include "libslic3r/MixedFilament.hpp"
|
|
#include "libslic3r/Print.hpp"
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
#include "libslic3r/Model.hpp"
|
|
#include "libslic3r/Slicing.hpp"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace Slic3r;
|
|
|
|
// Finding 1 — the helper computing max supported filament ID after a 3MF
|
|
// project-config load must include enabled mixed rows.
|
|
TEST_CASE("[review-fixes] mixed-aware max filament id", "[review-fixes]")
|
|
{
|
|
MixedFilamentManager mgr;
|
|
const size_t physical_count = 3;
|
|
const std::vector<std::string> colors = {"#FF0000", "#00FF00", "#0000FF"};
|
|
|
|
// No mixed rows: max == physical.
|
|
REQUIRE(mgr.total_filaments(physical_count) == physical_count);
|
|
|
|
// Add an enabled mixed row spanning physical 1 + 2 (custom + enabled by default).
|
|
mgr.add_custom_filament(1u, 2u, 50, colors);
|
|
REQUIRE(mgr.mixed_filaments().size() == 1);
|
|
REQUIRE(mgr.total_filaments(physical_count) == physical_count + 1);
|
|
|
|
// A disabled row does not contribute.
|
|
mgr.add_custom_filament(1u, 2u, 25, colors);
|
|
REQUIRE(mgr.mixed_filaments().size() == 2);
|
|
mgr.mixed_filaments().back().enabled = false;
|
|
REQUIRE(mgr.total_filaments(physical_count) == physical_count + 1);
|
|
|
|
// A deleted row also does not contribute (enabled_count() filters deleted).
|
|
mgr.add_custom_filament(2u, 3u, 50, colors);
|
|
REQUIRE(mgr.mixed_filaments().size() == 3);
|
|
mgr.mixed_filaments().back().deleted = true;
|
|
mgr.mixed_filaments().back().enabled = false;
|
|
REQUIRE(mgr.total_filaments(physical_count) == physical_count + 1);
|
|
}
|
|
|
|
// Finding 2 — passing nullptr for the print object must keep the legacy
|
|
// behaviour: no mixed gradient or dithering ranges are applied, and the
|
|
// profile is still seeded from the slicing parameters.
|
|
TEST_CASE("[review-fixes] update_layer_height_profile passthrough without print object",
|
|
"[review-fixes]")
|
|
{
|
|
Model model;
|
|
ModelObject *mo = model.add_object();
|
|
REQUIRE(mo != nullptr);
|
|
|
|
SlicingParameters sp;
|
|
sp.layer_height = 0.2;
|
|
sp.first_object_layer_height = 0.2;
|
|
sp.object_print_z_min = 0.0;
|
|
sp.object_print_z_uncompensated_max = 10.0;
|
|
|
|
std::vector<coordf_t> profile;
|
|
const bool updated = PrintObject::update_layer_height_profile(*mo, sp, profile, nullptr);
|
|
REQUIRE(updated);
|
|
REQUIRE(!profile.empty());
|
|
|
|
// Strong check: the nullptr branch must be a passthrough to
|
|
// layer_height_profile_from_ranges with the model_object's own
|
|
// layer_config_ranges — i.e. no mixed-gradient or dithering overrides
|
|
// were applied. Computing the expected profile via the same call the
|
|
// function performs internally lets a regression that silently sneaks
|
|
// those overrides into the nullptr path fail this test.
|
|
const std::vector<coordf_t> expected =
|
|
Slic3r::layer_height_profile_from_ranges(sp, mo->layer_config_ranges);
|
|
REQUIRE(profile == expected);
|
|
}
|
|
|
|
// Finding 4 — ToolOrdering used to push raw 1-based virtual mixed-filament IDs
|
|
// directly onto layer_tools.extruders, which then got decremented as if they
|
|
// were physical extruder IDs. The fix routes those IDs through the
|
|
// MixedFilamentManager so that virtual IDs collapse to a real physical extruder
|
|
// while physical IDs pass through unchanged.
|
|
TEST_CASE("[review-fixes] resolve_mixed_1based passthrough on physical id",
|
|
"[review-fixes]")
|
|
{
|
|
MixedFilamentManager mgr;
|
|
const std::vector<std::string> colors = {"#FF0000", "#00FF00"};
|
|
// Add a custom mixed row spanning physical 1 + 2.
|
|
mgr.add_custom_filament(1u, 2u, 50, colors);
|
|
REQUIRE(mgr.mixed_filaments().size() == 1);
|
|
|
|
const size_t num_physical = colors.size();
|
|
|
|
// Physical id (1) must passthrough.
|
|
REQUIRE(mgr.resolve(1u, num_physical, /*layer_index=*/0, /*print_z=*/0.f, /*layer_height=*/0.2f) == 1u);
|
|
REQUIRE(mgr.resolve(2u, num_physical, /*layer_index=*/0, /*print_z=*/0.f, /*layer_height=*/0.2f) == 2u);
|
|
|
|
// Virtual id (3 = 2 physical + 1 mixed) must resolve to 1 or 2 depending on cadence.
|
|
const unsigned int resolved = mgr.resolve(3u, num_physical, 0, 0.f, 0.2f);
|
|
REQUIRE((resolved == 1u || resolved == 2u));
|
|
}
|
|
|
|
TEST_CASE("[review-fixes] mixed/dithering option keys exist in PrintConfig",
|
|
"[review-fixes]")
|
|
{
|
|
// Sanity guard for finding 6: any key the porter wires into invalidation
|
|
// must resolve in the config. Build a default-populated DynamicPrintConfig
|
|
// that contains every print_config_def key, then probe the option lookup.
|
|
static const std::vector<std::string> keys = {
|
|
"mixed_filament_definitions",
|
|
"mixed_filament_gradient_mode",
|
|
"mixed_filament_height_lower_bound",
|
|
"mixed_filament_height_upper_bound",
|
|
"mixed_filament_advanced_dithering",
|
|
"mixed_filament_component_bias_enabled",
|
|
"mixed_filament_surface_indentation",
|
|
"mixed_filament_region_collapse",
|
|
"dithering_z_step_size",
|
|
"dithering_local_z_mode",
|
|
"dithering_local_z_whole_objects",
|
|
"dithering_local_z_direct_multicolor",
|
|
"dithering_step_painted_zones_only",
|
|
};
|
|
|
|
std::unique_ptr<DynamicPrintConfig> cfg(DynamicPrintConfig::new_from_defaults_keys(keys));
|
|
REQUIRE(cfg != nullptr);
|
|
|
|
for (const std::string &k : keys) {
|
|
INFO("missing config key: " << k);
|
|
REQUIRE(cfg->option(k) != nullptr);
|
|
// And the canonical print_config_def must know about it.
|
|
REQUIRE(print_config_def.get(k) != nullptr);
|
|
}
|
|
}
|