diff --git a/README.md b/README.md index 29c3c6fd886..df8ed95e323 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,8 @@ Zoomed-out G-code preview: - **Load OBJ files with image textures or vertex colors** - slice in full image resolution, no need to subdivide your model or bake vertex colors first -### Fast multicolor printing -- When printing image textures with this fork, only a single color is used per layer (not dependent on texture color). You can print many different models at once without increasing the number of tool changes. +### Print with only one tool change per layer +- When printing image textures with this technique, only a single filament color is used per layer (not dependent on texture color). You can print many different models at once without increasing the number of tool changes. ![More Test Prints](https://gradients.garden/gitlab_images/test_prints.jpeg) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 632e7fe0a81..201919f1a21 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -5941,9 +5941,11 @@ static bool texture_mapping_component_is_black_for_gcode(size_t int filament_color_mode, const std::vector> &component_colors) { - switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorBW))) { + switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorCMYK): case int(TextureMappingZone::FilamentColorRGBK): + case int(TextureMappingZone::FilamentColorCMYKW): + case int(TextureMappingZone::FilamentColorRGBKW): if (component_idx == 3) return true; break; @@ -6872,7 +6874,7 @@ static std::array raw_offset_preview_rgba_for_gcode(const std::vector< static std::vector raw_filament_color_mode_channel_keys_for_gcode(int filament_color_mode, size_t component_count) { std::vector keys; - switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorBW))) { + switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): keys = { "R", "G", "B" }; break; @@ -6894,6 +6896,12 @@ static std::vector raw_filament_color_mode_channel_keys_for_gcode(i case int(TextureMappingZone::FilamentColorBW): keys = { "K", "W" }; break; + case int(TextureMappingZone::FilamentColorCMYKW): + keys = { "C", "M", "Y", "K", "W" }; + break; + case int(TextureMappingZone::FilamentColorRGBKW): + keys = { "R", "G", "B", "K", "W" }; + break; default: break; } @@ -7130,6 +7138,24 @@ static std::vector semantic_component_indices_for_gcode(const std::vecto case int(TextureMappingZone::FilamentColorRGBW): semantic_colors = { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } }, { { 1.f, 1.f, 1.f } } }; break; + case int(TextureMappingZone::FilamentColorCMYKW): + semantic_colors = { + { { 0.f, 1.f, 1.f } }, + { { 1.f, 0.f, 1.f } }, + { { 1.f, 1.f, 0.f } }, + { { 0.f, 0.f, 0.f } }, + { { 1.f, 1.f, 1.f } } + }; + break; + case int(TextureMappingZone::FilamentColorRGBKW): + semantic_colors = { + { { 1.f, 0.f, 0.f } }, + { { 0.f, 1.f, 0.f } }, + { { 0.f, 0.f, 1.f } }, + { { 0.f, 0.f, 0.f } }, + { { 1.f, 1.f, 1.f } } + }; + break; default: return {}; } @@ -7141,7 +7167,7 @@ static std::vector> fixed_color_generic_solver_component_co { switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW))) { + int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): return { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } } }; case int(TextureMappingZone::FilamentColorCMY): @@ -7154,6 +7180,10 @@ static std::vector> fixed_color_generic_solver_component_co return { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } }, { { 0.f, 0.f, 0.f } } }; case int(TextureMappingZone::FilamentColorRGBW): return { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } }, { { 1.f, 1.f, 1.f } } }; + case int(TextureMappingZone::FilamentColorCMYKW): + return { { { 0.f, 1.f, 1.f } }, { { 1.f, 0.f, 1.f } }, { { 1.f, 1.f, 0.f } }, { { 0.f, 0.f, 0.f } }, { { 1.f, 1.f, 1.f } } }; + case int(TextureMappingZone::FilamentColorRGBKW): + return { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } }, { { 0.f, 0.f, 0.f } }, { { 1.f, 1.f, 1.f } } }; default: return {}; } @@ -7167,7 +7197,7 @@ static std::vector optimized_primary_component_weights_for_target_for_gco { const int clamped_mode = std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW)); + int(TextureMappingZone::FilamentColorRGBKW)); if (clamped_mode == int(TextureMappingZone::FilamentColorAny)) return {}; @@ -7237,6 +7267,33 @@ static std::vector optimized_primary_component_weights_for_target_for_gco return weights; } + if (clamped_mode == int(TextureMappingZone::FilamentColorCMYKW)) { + if (component_count != 5) + return {}; + const float chroma = std::max(0.f, 1.f - darkness - whiteness); + const float c = chroma <= EPSILON ? 0.f : 1.f - safe_div(r - whiteness, chroma); + const float m = chroma <= EPSILON ? 0.f : 1.f - safe_div(g - whiteness, chroma); + const float y = chroma <= EPSILON ? 0.f : 1.f - safe_div(b - whiteness, chroma); + weights[component_index_for_role(0)] = print_visibility_strength(c); + weights[component_index_for_role(1)] = print_visibility_strength(m); + weights[component_index_for_role(2)] = print_visibility_strength(y); + weights[component_index_for_role(3)] = print_visibility_strength(darkness); + weights[component_index_for_role(4)] = print_visibility_strength(whiteness); + return weights; + } + + if (clamped_mode == int(TextureMappingZone::FilamentColorRGBKW)) { + if (component_count != 5) + return {}; + const float chroma = std::max(0.f, 1.f - darkness - whiteness); + weights[component_index_for_role(0)] = print_visibility_strength(safe_div(r - whiteness, chroma)); + weights[component_index_for_role(1)] = print_visibility_strength(safe_div(g - whiteness, chroma)); + weights[component_index_for_role(2)] = print_visibility_strength(safe_div(b - whiteness, chroma)); + weights[component_index_for_role(3)] = print_visibility_strength(darkness); + weights[component_index_for_role(4)] = print_visibility_strength(whiteness); + return weights; + } + if (component_count != 4) return {}; @@ -8217,7 +8274,7 @@ std::optional GCode::texture_mapping_seam_hiding_point(const ExtrusionLoo const int texture_filament_color_mode = std::clamp(zone->filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW)); + int(TextureMappingZone::FilamentColorRGBKW)); const bool raw_texture_mapping_mode = zone->texture_mapping_mode == int(TextureMappingZone::TextureMappingRawValues); const bool texture_force_sequential_filaments = zone->force_sequential_filaments; @@ -9372,7 +9429,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, const int texture_filament_color_mode = std::clamp( zone->filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW)); + int(TextureMappingZone::FilamentColorRGBKW)); const bool texture_force_sequential_filaments = zone->force_sequential_filaments; const int generic_solver_lookup_mode = std::clamp(zone->generic_solver_lookup_mode, int(TextureMappingZone::GenericSolverClosestMix), diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 218768ac8d0..a2bed583ac0 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -37,7 +37,9 @@ struct PrimeTowerTextureRenderSettings RGB, RGBK, RGBW, - BW + BW, + CMYKW, + RGBKW }; bool enabled = false; @@ -207,6 +209,18 @@ private: std::array{1.f, 1.f, 1.f}}; case BW: return {std::array{0.f, 0.f, 0.f}, std::array{1.f, 1.f, 1.f}}; + case CMYKW: + return {std::array{0.f, 1.f, 1.f}, + std::array{1.f, 0.f, 1.f}, + std::array{1.f, 1.f, 0.f}, + std::array{0.f, 0.f, 0.f}, + std::array{1.f, 1.f, 1.f}}; + case RGBKW: + return {std::array{1.f, 0.f, 0.f}, + std::array{0.f, 1.f, 0.f}, + std::array{0.f, 0.f, 1.f}, + std::array{0.f, 0.f, 0.f}, + std::array{1.f, 1.f, 1.f}}; default: return {}; } } @@ -357,6 +371,26 @@ private: default: break; } + if (component_count == 5) { + const float chroma = std::max(0.f, 1.f - darkness - whiteness); + if (mode == int(TextureMappingZone::FilamentColorCMYKW)) { + const float c = chroma <= 1e-6f ? 0.f : 1.f - safe_div(r - whiteness, chroma); + const float m = chroma <= 1e-6f ? 0.f : 1.f - safe_div(g - whiteness, chroma); + const float y = chroma <= 1e-6f ? 0.f : 1.f - safe_div(b - whiteness, chroma); + return {print_visibility_strength(c), + print_visibility_strength(m), + print_visibility_strength(y), + print_visibility_strength(darkness), + print_visibility_strength(whiteness)}; + } + if (mode == int(TextureMappingZone::FilamentColorRGBKW)) { + return {print_visibility_strength(safe_div(r - whiteness, chroma)), + print_visibility_strength(safe_div(g - whiteness, chroma)), + print_visibility_strength(safe_div(b - whiteness, chroma)), + print_visibility_strength(darkness), + print_visibility_strength(whiteness)}; + } + } if (component_count != 4) return {}; if (mode == int(TextureMappingZone::FilamentColorCMYK)) { @@ -530,6 +564,29 @@ private: print_visibility_strength(gray <= 0.5f ? 2.f * gray : 1.f)}; break; } + case CMYKW: { + const float chroma = std::max(0.f, 1.f - darkness - whiteness); + const float c = chroma <= 1e-6f ? 0.f : 1.f - safe_div(r - whiteness, chroma); + const float m = chroma <= 1e-6f ? 0.f : 1.f - safe_div(g - whiteness, chroma); + const float y = chroma <= 1e-6f ? 0.f : 1.f - safe_div(b - whiteness, chroma); + ideals = {{{0.f, 1.f, 1.f}, {1.f, 0.f, 1.f}, {1.f, 1.f, 0.f}, {0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}}; + weights = {print_visibility_strength(c), + print_visibility_strength(m), + print_visibility_strength(y), + print_visibility_strength(darkness), + print_visibility_strength(whiteness)}; + break; + } + case RGBKW: { + const float chroma = std::max(0.f, 1.f - darkness - whiteness); + ideals = {{{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}, {0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}}; + weights = {print_visibility_strength(safe_div(r - whiteness, chroma)), + print_visibility_strength(safe_div(g - whiteness, chroma)), + print_visibility_strength(safe_div(b - whiteness, chroma)), + print_visibility_strength(darkness), + print_visibility_strength(whiteness)}; + break; + } default: ideals = {{{0.f, 1.f, 1.f}, {1.f, 0.f, 1.f}, {1.f, 1.f, 0.f}, {0.f, 0.f, 0.f}}}; weights = {print_visibility_strength(1.f - r), diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 60a833e2847..89b3a1916b3 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -137,6 +137,10 @@ static std::vector> prime_tower_semantic_colors_for_print(i return {{{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}, {1.f, 1.f, 1.f}}}; case PrimeTowerTextureRenderSettings::BW: return {{{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}}; + case PrimeTowerTextureRenderSettings::CMYKW: + return {{{0.f, 1.f, 1.f}, {1.f, 0.f, 1.f}, {1.f, 1.f, 0.f}, {0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}}; + case PrimeTowerTextureRenderSettings::RGBKW: + return {{{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}, {0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}}; default: return {}; } @@ -210,6 +214,8 @@ static int prime_tower_auto_color_mode_for_print(const ToolOrdering &tool_orderi PrimeTowerTextureRenderSettings::CMYW, PrimeTowerTextureRenderSettings::RGBK, PrimeTowerTextureRenderSettings::RGBW, + PrimeTowerTextureRenderSettings::CMYKW, + PrimeTowerTextureRenderSettings::RGBKW, PrimeTowerTextureRenderSettings::CMY, PrimeTowerTextureRenderSettings::RGB, PrimeTowerTextureRenderSettings::BW @@ -236,7 +242,7 @@ static int prime_tower_auto_color_mode_for_print(const ToolOrdering &tool_orderi static int prime_tower_texture_zone_color_mode_for_print(int filament_color_mode) { - switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorBW))) { + switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): return PrimeTowerTextureRenderSettings::RGB; case int(TextureMappingZone::FilamentColorCMY): return PrimeTowerTextureRenderSettings::CMY; case int(TextureMappingZone::FilamentColorCMYK): return PrimeTowerTextureRenderSettings::CMYK; @@ -244,6 +250,8 @@ static int prime_tower_texture_zone_color_mode_for_print(int filament_color_mode case int(TextureMappingZone::FilamentColorRGBK): return PrimeTowerTextureRenderSettings::RGBK; case int(TextureMappingZone::FilamentColorRGBW): return PrimeTowerTextureRenderSettings::RGBW; case int(TextureMappingZone::FilamentColorBW): return PrimeTowerTextureRenderSettings::BW; + case int(TextureMappingZone::FilamentColorCMYKW): return PrimeTowerTextureRenderSettings::CMYKW; + case int(TextureMappingZone::FilamentColorRGBKW): return PrimeTowerTextureRenderSettings::RGBKW; default: return PrimeTowerTextureRenderSettings::GenericSolver; } } @@ -258,6 +266,8 @@ static int prime_tower_texture_mapping_color_mode_for_print(int prime_tower_colo case PrimeTowerTextureRenderSettings::RGBK: return int(TextureMappingZone::FilamentColorRGBK); case PrimeTowerTextureRenderSettings::RGBW: return int(TextureMappingZone::FilamentColorRGBW); case PrimeTowerTextureRenderSettings::BW: return int(TextureMappingZone::FilamentColorBW); + case PrimeTowerTextureRenderSettings::CMYKW: return int(TextureMappingZone::FilamentColorCMYKW); + case PrimeTowerTextureRenderSettings::RGBKW: return int(TextureMappingZone::FilamentColorRGBKW); default: return int(TextureMappingZone::FilamentColorAny); } } @@ -3468,6 +3478,10 @@ void Print::_make_wipe_tower() texture.color_mode = PrimeTowerTextureRenderSettings::RGBW; else if (mode == "bw") texture.color_mode = PrimeTowerTextureRenderSettings::BW; + else if (mode == "cmykw") + texture.color_mode = PrimeTowerTextureRenderSettings::CMYKW; + else if (mode == "rgbkw") + texture.color_mode = PrimeTowerTextureRenderSettings::RGBKW; else texture.color_mode = PrimeTowerTextureRenderSettings::CMYK; diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 0fa8e2c4fa7..158f3c2ed3a 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -265,6 +265,10 @@ static const char *vertex_color_mode_name_for_error(int filament_color_mode) return "RGBW"; case int(TextureMappingZone::FilamentColorBW): return "BW"; + case int(TextureMappingZone::FilamentColorCMYKW): + return "CMYKW"; + case int(TextureMappingZone::FilamentColorRGBKW): + return "RGBKW"; default: return "Generic Solver (slow)"; } @@ -304,7 +308,7 @@ static std::vector collect_texture_mapping_vertex_color_mode_mismat const int filament_color_mode = std::clamp(zone->filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW)); + int(TextureMappingZone::FilamentColorRGBKW)); const size_t expected_count = TextureMappingManager::expected_component_count(zone->texture_mapping_mode, filament_color_mode); if (expected_count == 0) diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index a9aba777f0e..c50c4f1953a 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -394,7 +394,7 @@ static std::vector semantic_colors(int filament_color_mode) { switch (clamp_int(filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW))) { + int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): return {{255, 0, 0}, {0, 255, 0}, {0, 0, 255}}; case int(TextureMappingZone::FilamentColorCMY): return {{0, 255, 255}, {255, 0, 255}, {255, 255, 0}}; case int(TextureMappingZone::FilamentColorCMYK): return {{0, 255, 255}, {255, 0, 255}, {255, 255, 0}, {0, 0, 0}}; @@ -402,6 +402,8 @@ static std::vector semantic_colors(int filament_color_mode) case int(TextureMappingZone::FilamentColorRGBK): return {{255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {0, 0, 0}}; case int(TextureMappingZone::FilamentColorRGBW): return {{255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {255, 255, 255}}; case int(TextureMappingZone::FilamentColorBW): return {{0, 0, 0}, {255, 255, 255}}; + case int(TextureMappingZone::FilamentColorCMYKW): return {{0, 255, 255}, {255, 0, 255}, {255, 255, 0}, {0, 0, 0}, {255, 255, 255}}; + case int(TextureMappingZone::FilamentColorRGBKW): return {{255, 0, 0}, {0, 255, 0}, {0, 0, 255}, {0, 0, 0}, {255, 255, 255}}; default: return {}; } } @@ -497,7 +499,7 @@ static int mapping_mode_from_name(const std::string &name) static std::string color_model_name(int mode) { - switch (clamp_int(mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorBW))) { + switch (clamp_int(mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): return "rgb"; case int(TextureMappingZone::FilamentColorCMY): return "cmy"; case int(TextureMappingZone::FilamentColorCMYK): return "cmyk"; @@ -505,6 +507,8 @@ static std::string color_model_name(int mode) case int(TextureMappingZone::FilamentColorRGBK): return "rgbk"; case int(TextureMappingZone::FilamentColorRGBW): return "rgbw"; case int(TextureMappingZone::FilamentColorBW): return "bw"; + case int(TextureMappingZone::FilamentColorCMYKW): return "cmykw"; + case int(TextureMappingZone::FilamentColorRGBKW): return "rgbkw"; default: return "any"; } } @@ -518,6 +522,8 @@ static int color_model_from_name(const std::string &name) if (name == "rgbk") return int(TextureMappingZone::FilamentColorRGBK); if (name == "rgbw") return int(TextureMappingZone::FilamentColorRGBW); if (name == "bw") return int(TextureMappingZone::FilamentColorBW); + if (name == "cmykw") return int(TextureMappingZone::FilamentColorCMYKW); + if (name == "rgbkw") return int(TextureMappingZone::FilamentColorRGBKW); return int(TextureMappingZone::FilamentColorAny); } @@ -620,7 +626,7 @@ static std::string normalized_prime_tower_color_mode_name(std::string name) if (name == "generic" || name == "generic_solver" || name == "solver") return "generic_solver"; if (name == "rgb" || name == "cmy" || name == "cmyk" || name == "cmyw" || - name == "rgbk" || name == "rgbw" || name == "bw") + name == "rgbk" || name == "rgbw" || name == "bw" || name == "cmykw" || name == "rgbkw") return name; return "auto"; } @@ -1344,7 +1350,7 @@ size_t TextureMappingManager::expected_component_count(int mapping_mode, int fil switch (clamp_int(filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW))) { + int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): case int(TextureMappingZone::FilamentColorCMY): return 3; @@ -1355,6 +1361,9 @@ size_t TextureMappingManager::expected_component_count(int mapping_mode, int fil return 4; case int(TextureMappingZone::FilamentColorBW): return 2; + case int(TextureMappingZone::FilamentColorCMYKW): + case int(TextureMappingZone::FilamentColorRGBKW): + return 5; default: return 0; } diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index befaa96a0de..2d7eca52332 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -52,7 +52,9 @@ struct TextureMappingZone FilamentColorCMYW = 4, FilamentColorRGBK = 5, FilamentColorRGBW = 6, - FilamentColorBW = 7 + FilamentColorBW = 7, + FilamentColorCMYKW = 8, + FilamentColorRGBKW = 9 }; enum GenericSolverLookupMode : uint8_t { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 9bf3641682f..58b40e67beb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -314,6 +314,10 @@ static std::string raw_atlas_color_mode_name_for_keys(const std::vector colors = { + ColorRGBA(0.f, 1.f, 1.f, 1.f), + ColorRGBA(1.f, 0.f, 1.f, 1.f), + ColorRGBA(1.f, 1.f, 0.f, 1.f), + ColorRGBA(0.f, 0.f, 0.f, 1.f), + ColorRGBA(1.f, 1.f, 1.f, 1.f) + }; + const std::vector weights = closest_color_mix_weights(colors, ColorRGBA(m_rgb_color[0], m_rgb_color[1], m_rgb_color[2], 1.f)); + for (size_t idx = 0; idx < m_cmykw_color.size() && idx < weights.size(); ++idx) + m_cmykw_color[idx] = weights[idx]; +} + +void GLGizmoTrueColorPainting::sync_rgb_from_cmykw() +{ + const std::vector colors = { + ColorRGBA(0.f, 1.f, 1.f, 1.f), + ColorRGBA(1.f, 0.f, 1.f, 1.f), + ColorRGBA(1.f, 1.f, 0.f, 1.f), + ColorRGBA(0.f, 0.f, 0.f, 1.f), + ColorRGBA(1.f, 1.f, 1.f, 1.f) + }; + const std::vector weights(m_cmykw_color.begin(), m_cmykw_color.end()); + const ColorRGBA mixed = color_mix_from_weights(colors, weights, ColorRGBA(1.f, 1.f, 1.f, 1.f)); + m_rgb_color[0] = mixed.r(); + m_rgb_color[1] = mixed.g(); + m_rgb_color[2] = mixed.b(); +} + +void GLGizmoTrueColorPainting::sync_rgbkw_from_rgb() +{ + const std::vector colors = { + ColorRGBA(1.f, 0.f, 0.f, 1.f), + ColorRGBA(0.f, 1.f, 0.f, 1.f), + ColorRGBA(0.f, 0.f, 1.f, 1.f), + ColorRGBA(0.f, 0.f, 0.f, 1.f), + ColorRGBA(1.f, 1.f, 1.f, 1.f) + }; + const std::vector weights = closest_color_mix_weights(colors, ColorRGBA(m_rgb_color[0], m_rgb_color[1], m_rgb_color[2], 1.f)); + for (size_t idx = 0; idx < m_rgbkw_color.size() && idx < weights.size(); ++idx) + m_rgbkw_color[idx] = weights[idx]; +} + +void GLGizmoTrueColorPainting::sync_rgb_from_rgbkw() +{ + const std::vector colors = { + ColorRGBA(1.f, 0.f, 0.f, 1.f), + ColorRGBA(0.f, 1.f, 0.f, 1.f), + ColorRGBA(0.f, 0.f, 1.f, 1.f), + ColorRGBA(0.f, 0.f, 0.f, 1.f), + ColorRGBA(1.f, 1.f, 1.f, 1.f) + }; + const std::vector weights(m_rgbkw_color.begin(), m_rgbkw_color.end()); + const ColorRGBA mixed = color_mix_from_weights(colors, weights, ColorRGBA(1.f, 1.f, 1.f, 1.f)); + m_rgb_color[0] = mixed.r(); + m_rgb_color[1] = mixed.g(); + m_rgb_color[2] = mixed.b(); +} + void GLGizmoTrueColorPainting::sync_bw_from_rgb() { const float r = std::clamp(m_rgb_color[0], 0.f, 1.f); @@ -8115,6 +8179,12 @@ void GLGizmoTrueColorPainting::sync_active_color_mode_from_rgb(bool update_filam case ColorInputMode::BW: sync_bw_from_rgb(); break; + case ColorInputMode::CMYKW: + sync_cmykw_from_rgb(); + break; + case ColorInputMode::RGBKW: + sync_rgbkw_from_rgb(); + break; } } @@ -8262,6 +8332,60 @@ bool GLGizmoTrueColorPainting::render_rgbw_picker(float item_width) return changed; } +bool GLGizmoTrueColorPainting::render_cmykw_picker(float item_width) +{ + bool changed = false; + ImGui::PushItemWidth(item_width); + ImGuiColorEditFlags flags = ImGuiColorEditFlags_DisplayRGB | + ImGuiColorEditFlags_InputRGB | + ImGuiColorEditFlags_NoInputs; + if (ImGui::ColorEdit3("##true_color_cmykw_visual", m_rgb_color.data(), flags)) { + sync_cmykw_from_rgb(); + changed = true; + } + + bool cmykw_changed = false; + cmykw_changed |= ImGui::SliderFloat("Cyan", &m_cmykw_color[0], 0.f, 1.f, "%.2f"); + cmykw_changed |= ImGui::SliderFloat("Magenta", &m_cmykw_color[1], 0.f, 1.f, "%.2f"); + cmykw_changed |= ImGui::SliderFloat("Yellow", &m_cmykw_color[2], 0.f, 1.f, "%.2f"); + cmykw_changed |= ImGui::SliderFloat("Black", &m_cmykw_color[3], 0.f, 1.f, "%.2f"); + cmykw_changed |= ImGui::SliderFloat("White", &m_cmykw_color[4], 0.f, 1.f, "%.2f"); + ImGui::PopItemWidth(); + + if (cmykw_changed) { + sync_rgb_from_cmykw(); + changed = true; + } + return changed; +} + +bool GLGizmoTrueColorPainting::render_rgbkw_picker(float item_width) +{ + bool changed = false; + ImGui::PushItemWidth(item_width); + ImGuiColorEditFlags flags = ImGuiColorEditFlags_DisplayRGB | + ImGuiColorEditFlags_InputRGB | + ImGuiColorEditFlags_NoInputs; + if (ImGui::ColorEdit3("##true_color_rgbkw_visual", m_rgb_color.data(), flags)) { + sync_rgbkw_from_rgb(); + changed = true; + } + + bool rgbkw_changed = false; + rgbkw_changed |= ImGui::SliderFloat("Red", &m_rgbkw_color[0], 0.f, 1.f, "%.2f"); + rgbkw_changed |= ImGui::SliderFloat("Green", &m_rgbkw_color[1], 0.f, 1.f, "%.2f"); + rgbkw_changed |= ImGui::SliderFloat("Blue", &m_rgbkw_color[2], 0.f, 1.f, "%.2f"); + rgbkw_changed |= ImGui::SliderFloat("Black", &m_rgbkw_color[3], 0.f, 1.f, "%.2f"); + rgbkw_changed |= ImGui::SliderFloat("White", &m_rgbkw_color[4], 0.f, 1.f, "%.2f"); + ImGui::PopItemWidth(); + + if (rgbkw_changed) { + sync_rgb_from_rgbkw(); + changed = true; + } + return changed; +} + bool GLGizmoTrueColorPainting::render_bw_picker(float item_width) { bool changed = false; @@ -8378,7 +8502,9 @@ void GLGizmoTrueColorPainting::on_render_input_window(float x, float y, float bo "CMYW", "RGBK", "RGBW", - "BW" + "BW", + "CMYKW", + "RGBKW" }; const int mode_count = int(sizeof(mode_labels) / sizeof(mode_labels[0])); int mode = std::clamp(int(m_color_input_mode), 0, mode_count - 1); @@ -8435,6 +8561,12 @@ void GLGizmoTrueColorPainting::on_render_input_window(float x, float y, float bo case ColorInputMode::BW: color_changed = render_bw_picker(slider_width); break; + case ColorInputMode::CMYKW: + color_changed = render_cmykw_picker(slider_width); + break; + case ColorInputMode::RGBKW: + color_changed = render_rgbkw_picker(slider_width); + break; } if (color_changed) update_triangle_selectors_color(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index afe3efa66d4..d20e0df63bf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -220,7 +220,9 @@ private: CMYW, RGBK, RGBW, - BW + BW, + CMYKW, + RGBKW }; bool on_init() override; @@ -257,6 +259,10 @@ private: void sync_rgb_from_rgbk(); void sync_rgbw_from_rgb(); void sync_rgb_from_rgbw(); + void sync_cmykw_from_rgb(); + void sync_rgb_from_cmykw(); + void sync_rgbkw_from_rgb(); + void sync_rgb_from_rgbkw(); void sync_bw_from_rgb(); void sync_rgb_from_bw(); void ensure_filament_mix_colors(); @@ -269,6 +275,8 @@ private: bool render_cmyw_picker(float item_width); bool render_rgbk_picker(float item_width); bool render_rgbw_picker(float item_width); + bool render_cmykw_picker(float item_width); + bool render_rgbkw_picker(float item_width); bool render_bw_picker(float item_width); bool render_filament_colors_picker(float item_width); @@ -279,6 +287,8 @@ private: std::array m_rgbk_color { 0.f, 0.f, 0.f, 1.f }; std::array m_rgbw_color { 0.f, 0.f, 0.f, 1.f }; std::array m_bw_color { 1.f, 0.f }; + std::array m_cmykw_color { 0.f, 0.f, 0.f, 0.f, 1.f }; + std::array m_rgbkw_color { 0.f, 0.f, 0.f, 0.f, 1.f }; std::vector m_filament_mix_colors; std::vector m_filament_mix; float m_brush_hardness = 1.f; diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp index 2a5597cc3dc..24d295b0c03 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp @@ -35,8 +35,8 @@ constexpr float k_preview_clip_padding = 2.f * k_preview_offset; constexpr float k_polygon_offset_factor = -1.f; constexpr float k_polygon_offset_units = -1.f; constexpr float k_epsilon = 1e-6f; -constexpr unsigned int k_simulated_texture_preview_max_edge = 1024; -constexpr size_t k_simulated_texture_preview_max_pixels = 1024ull * 1024ull; +constexpr unsigned int k_simulated_texture_preview_max_edge = 2048; +constexpr size_t k_simulated_texture_preview_max_pixels = 2048ull * 2048ull; constexpr const char *TEXTURE_MAPPING_BACKGROUND_COLOR_CONFIG_KEY = "texture_mapping_background_color"; struct TexturePreviewMixCandidate @@ -556,7 +556,7 @@ std::vector raw_filament_color_mode_channel_keys_for_texture_previe std::vector keys; switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW))) { + int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): keys = { "R", "G", "B" }; break; @@ -578,6 +578,12 @@ std::vector raw_filament_color_mode_channel_keys_for_texture_previe case int(TextureMappingZone::FilamentColorBW): keys = { "K", "W" }; break; + case int(TextureMappingZone::FilamentColorCMYKW): + keys = { "C", "M", "Y", "K", "W" }; + break; + case int(TextureMappingZone::FilamentColorRGBKW): + keys = { "R", "G", "B", "K", "W" }; + break; default: break; } @@ -771,6 +777,24 @@ std::vector semantic_component_indices_for_texture_preview(const std::ve case int(TextureMappingZone::FilamentColorRGBW): semantic_colors = { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } }, { { 1.f, 1.f, 1.f } } }; break; + case int(TextureMappingZone::FilamentColorCMYKW): + semantic_colors = { + { { 0.f, 1.f, 1.f } }, + { { 1.f, 0.f, 1.f } }, + { { 1.f, 1.f, 0.f } }, + { { 0.f, 0.f, 0.f } }, + { { 1.f, 1.f, 1.f } } + }; + break; + case int(TextureMappingZone::FilamentColorRGBKW): + semantic_colors = { + { { 1.f, 0.f, 0.f } }, + { { 0.f, 1.f, 0.f } }, + { { 0.f, 0.f, 1.f } }, + { { 0.f, 0.f, 0.f } }, + { { 1.f, 1.f, 1.f } } + }; + break; default: return {}; } @@ -782,7 +806,7 @@ std::vector> fixed_color_generic_solver_component_colors(in { switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW))) { + int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): return { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } } }; case int(TextureMappingZone::FilamentColorCMY): @@ -795,6 +819,10 @@ std::vector> fixed_color_generic_solver_component_colors(in return { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } }, { { 0.f, 0.f, 0.f } } }; case int(TextureMappingZone::FilamentColorRGBW): return { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } }, { { 1.f, 1.f, 1.f } } }; + case int(TextureMappingZone::FilamentColorCMYKW): + return { { { 0.f, 1.f, 1.f } }, { { 1.f, 0.f, 1.f } }, { { 1.f, 1.f, 0.f } }, { { 0.f, 0.f, 0.f } }, { { 1.f, 1.f, 1.f } } }; + case int(TextureMappingZone::FilamentColorRGBKW): + return { { { 1.f, 0.f, 0.f } }, { { 0.f, 1.f, 0.f } }, { { 0.f, 0.f, 1.f } }, { { 0.f, 0.f, 0.f } }, { { 1.f, 1.f, 1.f } } }; default: return {}; } @@ -827,7 +855,7 @@ bool texture_preview_uses_generic_solver(const TexturePreviewSimulationSettings const int clamped_mode = std::clamp(settings.filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW)); + int(TextureMappingZone::FilamentColorRGBKW)); size_t expected_component_count = 0; switch (clamped_mode) { case int(TextureMappingZone::FilamentColorRGB): @@ -843,6 +871,10 @@ bool texture_preview_uses_generic_solver(const TexturePreviewSimulationSettings case int(TextureMappingZone::FilamentColorBW): expected_component_count = 2; break; + case int(TextureMappingZone::FilamentColorCMYKW): + case int(TextureMappingZone::FilamentColorRGBKW): + expected_component_count = 5; + break; default: return true; } @@ -1183,7 +1215,7 @@ std::vector optimized_primary_component_weights_for_target(const std::arr { const int clamped_mode = std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW)); + int(TextureMappingZone::FilamentColorRGBKW)); if (clamped_mode == int(TextureMappingZone::FilamentColorAny)) return {}; @@ -1249,6 +1281,33 @@ std::vector optimized_primary_component_weights_for_target(const std::arr return weights; } + if (clamped_mode == int(TextureMappingZone::FilamentColorCMYKW)) { + if (component_count != 5) + return {}; + const float chroma = std::max(0.f, 1.f - darkness - whiteness); + const float c = chroma <= k_epsilon ? 0.f : 1.f - safe_div(r - whiteness, chroma); + const float m = chroma <= k_epsilon ? 0.f : 1.f - safe_div(g - whiteness, chroma); + const float y = chroma <= k_epsilon ? 0.f : 1.f - safe_div(b - whiteness, chroma); + weights[component_index_for_role(0)] = print_visibility_strength(c * chroma); + weights[component_index_for_role(1)] = print_visibility_strength(m * chroma); + weights[component_index_for_role(2)] = print_visibility_strength(y * chroma); + weights[component_index_for_role(3)] = print_visibility_strength(darkness); + weights[component_index_for_role(4)] = clamp01(std::pow(whiteness, 1.35f)); + return weights; + } + + if (clamped_mode == int(TextureMappingZone::FilamentColorRGBKW)) { + if (component_count != 5) + return {}; + const float chroma = std::max(0.f, 1.f - darkness - whiteness); + weights[component_index_for_role(0)] = print_visibility_strength(safe_div(r - whiteness, chroma) * chroma); + weights[component_index_for_role(1)] = print_visibility_strength(safe_div(g - whiteness, chroma) * chroma); + weights[component_index_for_role(2)] = print_visibility_strength(safe_div(b - whiteness, chroma) * chroma); + weights[component_index_for_role(3)] = print_visibility_strength(darkness); + weights[component_index_for_role(4)] = clamp01(std::pow(whiteness, 1.35f)); + return weights; + } + if (component_count != 4) return {}; @@ -1420,7 +1479,7 @@ std::optional texture_preview_simulation_setti int(TextureMappingZone::TextureMappingRawValues)); settings.filament_color_mode = std::clamp(zone->filament_color_mode, int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW)); + int(TextureMappingZone::FilamentColorRGBKW)); settings.force_sequential_filaments = zone->force_sequential_filaments; settings.limit_texture_resolution = zone->preview_limit_resolution; settings.compact_offset_mode = zone->compact_offset_mode; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a49c0b0eb99..a0953a67edd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -612,12 +612,49 @@ static wxArrayString texture_mapping_color_mode_choices() choices.Add(_L("RGBK")); choices.Add(_L("RGBW")); choices.Add(_L("BW")); + choices.Add(_L("5+ toolheads:")); + choices.Add(_L("CMYKW")); + choices.Add(_L("RGBKW")); return choices; } +static int texture_mapping_five_toolhead_marker_selection() +{ + return 8; +} + +static int texture_mapping_color_mode_selection(int filament_color_mode) +{ + const int clamped_mode = std::clamp(filament_color_mode, + int(TextureMappingZone::FilamentColorAny), + int(TextureMappingZone::FilamentColorRGBKW)); + switch (clamped_mode) { + case int(TextureMappingZone::FilamentColorCMYKW): return 9; + case int(TextureMappingZone::FilamentColorRGBKW): return 10; + default: return clamped_mode; + } +} + +static int texture_mapping_color_mode_from_selection(int selection, int fallback) +{ + switch (selection) { + case 0: return int(TextureMappingZone::FilamentColorAny); + case 1: return int(TextureMappingZone::FilamentColorRGB); + case 2: return int(TextureMappingZone::FilamentColorCMY); + case 3: return int(TextureMappingZone::FilamentColorCMYK); + case 4: return int(TextureMappingZone::FilamentColorCMYW); + case 5: return int(TextureMappingZone::FilamentColorRGBK); + case 6: return int(TextureMappingZone::FilamentColorRGBW); + case 7: return int(TextureMappingZone::FilamentColorBW); + case 9: return int(TextureMappingZone::FilamentColorCMYKW); + case 10: return int(TextureMappingZone::FilamentColorRGBKW); + default: return std::clamp(fallback, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorRGBKW)); + } +} + static std::vector texture_mapping_channel_labels(int filament_color_mode) { - switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorBW))) { + switch (std::clamp(filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorRGBKW))) { case int(TextureMappingZone::FilamentColorRGB): return {_L("Red"), _L("Green"), _L("Blue")}; case int(TextureMappingZone::FilamentColorCMY): @@ -632,6 +669,10 @@ static std::vector texture_mapping_channel_labels(int filament_color_m return {_L("Red"), _L("Green"), _L("Blue"), _L("White")}; case int(TextureMappingZone::FilamentColorBW): return {_L("Black"), _L("White")}; + case int(TextureMappingZone::FilamentColorCMYKW): + return {_L("Cyan"), _L("Magenta"), _L("Yellow"), _L("Black"), _L("White")}; + case int(TextureMappingZone::FilamentColorRGBKW): + return {_L("Red"), _L("Green"), _L("Blue"), _L("Black"), _L("White")}; default: return {}; } @@ -1348,8 +1389,23 @@ public: prime_color_modes.Add(_L("RGBK")); prime_color_modes.Add(_L("RGBW")); prime_color_modes.Add(_L("BW")); + prime_color_modes.Add(_L("5+ toolheads:")); + prime_color_modes.Add(_L("CMYKW")); + prime_color_modes.Add(_L("RGBKW")); m_prime_tower_color_mode_choice = new wxChoice(global_page, wxID_ANY, wxDefaultPosition, wxDefaultSize, prime_color_modes); m_prime_tower_color_mode_choice->SetSelection(prime_tower_color_mode_selection(m_global_settings.prime_tower_color_mode)); + int last_prime_tower_color_mode_selection = m_prime_tower_color_mode_choice->GetSelection(); + m_prime_tower_color_mode_choice->Bind(wxEVT_CHOICE, + [this, last_prime_tower_color_mode_selection](wxCommandEvent &) mutable { + if (m_prime_tower_color_mode_choice == nullptr) + return; + const int selection = m_prime_tower_color_mode_choice->GetSelection(); + if (selection == prime_tower_five_toolhead_marker_selection()) { + m_prime_tower_color_mode_choice->SetSelection(last_prime_tower_color_mode_selection); + return; + } + last_prime_tower_color_mode_selection = selection; + }); color_mode_row->Add(m_prime_tower_color_mode_choice, 1, wxALIGN_CENTER_VERTICAL); global_root->Add(color_mode_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap); @@ -1555,10 +1611,17 @@ private: case 6: return "rgbk"; case 7: return "rgbw"; case 8: return "bw"; + case 10: return "cmykw"; + case 11: return "rgbkw"; default: return "auto"; } } + static int prime_tower_five_toolhead_marker_selection() + { + return 9; + } + static int prime_tower_color_mode_selection(const std::string &mode) { const std::string normalized = TextureMappingGlobalSettings::normalize_color_mode_name(mode); @@ -1570,6 +1633,8 @@ private: if (normalized == "rgbk") return 6; if (normalized == "rgbw") return 7; if (normalized == "bw") return 8; + if (normalized == "cmykw") return 10; + if (normalized == "rgbkw") return 11; return 0; } @@ -5489,7 +5554,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) auto *mode_row = new wxWrapSizer(wxHORIZONTAL, wxWRAPSIZER_DEFAULT_FLAGS); mode_row->Add(new wxStaticText(editor, wxID_ANY, _L("Filament colors")), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap); auto *mode_choice = new wxChoice(editor, wxID_ANY, wxDefaultPosition, wxDefaultSize, texture_mapping_color_mode_choices()); - mode_choice->SetSelection(std::clamp(entry.filament_color_mode, int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorBW))); + mode_choice->SetSelection(texture_mapping_color_mode_selection(entry.filament_color_mode)); mode_row->Add(mode_choice, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, gap); auto *preview_colors_chk = new wxCheckBox(editor, wxID_ANY, _L("Preview Result Colors")); preview_colors_chk->SetValue(entry.preview_simulate_colors); @@ -5535,7 +5600,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) updated.component_a = ids.empty() ? 1 : ids.front(); updated.component_b = ids.size() > 1 ? ids[1] : updated.component_a; updated.filament_color_mode = mode_choice != nullptr ? - std::clamp(mode_choice->GetSelection(), int(TextureMappingZone::FilamentColorAny), int(TextureMappingZone::FilamentColorBW)) : + texture_mapping_color_mode_from_selection(mode_choice->GetSelection(), updated.filament_color_mode) : updated.filament_color_mode; updated.preview_simulate_colors = preview_colors_chk != nullptr && preview_colors_chk->GetValue(); updated.contrast_pct = contrast_spin != nullptr ? std::clamp(float(contrast_spin->GetValue()), 25.f, 300.f) : updated.contrast_pct; @@ -5576,14 +5641,22 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) for (wxCheckBox *chk : filament_checks) if (chk != nullptr) chk->Bind(wxEVT_CHECKBOX, [apply_controls](wxCommandEvent &) { apply_controls(); }); - mode_choice->Bind(wxEVT_CHOICE, [this, zone_index, mgr_ptr, num_physical, physical_colors, filament_checks, mode_choice, apply_controls](wxCommandEvent &) { + int last_mode_selection = mode_choice->GetSelection(); + mode_choice->Bind(wxEVT_CHOICE, [this, zone_index, mgr_ptr, num_physical, physical_colors, filament_checks, mode_choice, apply_controls, last_mode_selection](wxCommandEvent &) mutable { + if (mode_choice != nullptr) { + const int selection = mode_choice->GetSelection(); + if (selection == texture_mapping_five_toolhead_marker_selection()) { + mode_choice->SetSelection(last_mode_selection); + return; + } + last_mode_selection = selection; + } if (zone_index < mgr_ptr->zones().size()) { TextureMappingZone &zone = mgr_ptr->zones()[zone_index]; if (zone.auto_adjust_filament_selection) { TextureMappingZone adjusted = zone; - adjusted.filament_color_mode = std::clamp(mode_choice != nullptr ? mode_choice->GetSelection() : zone.filament_color_mode, - int(TextureMappingZone::FilamentColorAny), - int(TextureMappingZone::FilamentColorBW)); + adjusted.filament_color_mode = texture_mapping_color_mode_from_selection( + mode_choice != nullptr ? mode_choice->GetSelection() : -1, zone.filament_color_mode); TextureMappingManager::auto_adjust_texture_component_ids(adjusted, num_physical, physical_colors); const std::vector adjusted_ids = texture_mapping_selected_ids(adjusted, num_physical); for (size_t idx = 0; idx < filament_checks.size(); ++idx)